Sinatra session.clear not working - ruby

I am programming a simple login/out web app. Here's the relevent code:
get '/logout' do
session.clear
end
get '/self' do
if session[:user_id]
user = User.find session[:user_id]
user.to_json
else
status 401
{"error" => "Not logged in."}.to_json
end
end
If I call logout, and then self, it doesn't return "Not logged in". Rather the :user_id still persists and I am returned the user's details. What am I doing wrong?
Thanks!

This should work
get '/logout' do
session[:user_id] = nil
redirect '/self'
emd

Related

Trying to prevent duplicate username creation in Sinatra app

Hi I am trying to prevent duplicate user name creation, and my application keeps trying to redirect me to a hypothetical account page and keeps throwing username undefined error in my erb file, and I am actually trying to redirect back to signup page if the user name already exist.
Here is my model for User class:
class User <ActiveRecord::Base
include Slugifiable::InstanceMethods
extend Slugifiable::ClassMethods
validates_uniqueness_of :username, case_sensitive: false <---This should validate if username exist
has_many :players
has_secure_password
end
Here is my user controller post method where it should either create or redirect back to signup page and give a flash message that the user name exist already
post '/signup' do
if params[:username] == "" || params[:email] == "" || params[:password] == "" || params[:name] == ""
flash[:notice] = "<h3 class ='alert'>Please fill-in info!</h3>"
redirect to '/signup'
else
if #user
flash[:notice] = "<h3>Username already exist, try anoter one.</h3>"
redirect to '/signup'
else
#user = User.create(:username => params[:username], :email => params[:email], :password =>
params[:password], :name => params[:name])
session[:user_id] = #user.id
redirect to '/account'
end
end
end
When I try to create a duplicate username for testing purposes....it keeps redirecting me to an account page but throws an error that username is undefined from erb file. It works fine when creating a completely new username though. What am I doing wrong?
"throws an error ... from erb" sounds like there's an issue in an ERB file somewhere?
User.create probably silently fails when the uniqueness check triggers, i.e. it sounds like it sets #user.username to nil, or #user itself is nil.
You also never defined what it should do when the validation check fails.
You can:
use User.create! to raise an exception.
check #user.valid? and #user.errors for any errors, and report them to the user

'Refused to display in frame.' Facebook Oauth dialog

I'm having some trouble authenticating users for my app. During the authentication process I get the following error:
Refused to display 'https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxxx…%2Fliketodownload.xx-xxxx.com%2Fauth%2Ffacebook%2Fcallback&scope=email' in a frame because it set 'X-Frame-Options' to 'DENY'.
I think it's to do with the authentication trying to redirected to an invalid target and that's why it's getting blocked. But with Ruby and Sinatra I am unsure of how to overcome this.
Many thanks.
UPDATE
I'm not redirecting to views, of which the auth and add to page dialogs are trigged into new targets via html. Now I'm trying to figure out the logic of which to auth and redirect users appropriately.
Code below:
post '/' do
if current_user
signed_request = FBGraph::Canvas.parse_signed_request(APP_SECRET, params[:signed_request])
if signed_request["page"] != nil
is_admin = signed_request["page"]["admin"]
is_liked = signed_request["page"]["liked"]
if is_admin #if admin, see if existing user is in db, if not create, then send to admin page
puts "user is a page admin" #logging for dev
redirect '/index'
elsif is_liked #if liked send to download end point
puts "user has liked page" #logging for dev purposes
redirect '/main/#/liked'
elsif !is_liked #otherwise make them like the page
puts "user has not liked" #logging for dev purposes
redirect '/main/#/notliked'
end
else
redirect '/addtopage/#/addtopageview'
end
elsif $auth1 && !current_user
puts "post / add to page view reached"
User.first_or_create({:uid => $auth1["uid"]}, {
:uid => $auth1["uid"],
:nickname => $auth1["info"]["nickname"],
:name => $auth1["info"]["name"],
:email_address => $auth1["info"]["email"],
:created_at => Time.now})
redirect '/addtopage/#/addtopageview'
else
# we just redirect to /auth/facebook here which will parse the #signed_request FB sends us, asking for auth if the user has not already granted access, or simply moving straight to the callback where they have already granted access.
puts "post / auth me reached"
redirect '/addtopage/#/authme'
end
end
get '/auth/:provider/callback' do
content_type 'application/json'
response.set_cookie 'test', {:value => "facebook_callback", :path => "/"}
JSON.generate(request.env)
auth = request.env["omniauth.auth"]
$auth1 = auth
#need escape here to allow user to initially authorise app without the full #signed_request?
session['fb_auth'] = auth
session['fb_token'] = cookies[:fb_token] = auth['credentials']['token']
session['fb_error'] = nil
if params[:signed_request] != nil #if the signed request isn't empty
signed_request = FBGraph::Canvas.parse_signed_request(APP_SECRET, params[:signed_request])
if signed_request["page"] != nil #if the signed request contains page data
$page_id = signed_request["page"]["id"]
is_admin = signed_request["page"]["admin"]
is_liked = signed_request["page"]["liked"]
if is_admin #if admin, see if existing user is in db, if not create, then send to admin page
puts "user is a page admin" #logging for dev
User.first_or_create({:uid => auth["uid"]}, {
:uid => auth["uid"],
:nickname => auth["info"]["nickname"],
:name => auth["info"]["name"],
:email_address => auth["info"]["email"],
:created_at => Time.now})
#insert page_id into database?
redirect '/index'
elsif is_liked #if liked send to download end point
puts "user has liked page" #logging for dev purposes
redirect '/main/#/liked'
elsif !is_liked #otherwise make them like the page
puts "user has not liked" #logging for dev purposes
redirect '/main/#/notliked'
end
else #user authed app but needs to add to page
puts "add to page view"
redirect '/addtopage/#/addtopageview'
end
else
#needs to redirect to a page telling them that they must be on facebook or that they must authorise the application
redirect '/index'
end
end
helpers do
def current_user
#current_user ||= User.get(session[:user_id]) if session[:user_id]
end
end
Facebook domains can not be iframed except for the socials plugins, Why?
for security reasons, for example let's say you're logged into Your Facebook account
and I have http://example.com/xss.html which has an iframe of http://facebook.com in this way I can steal or hi-jack sensitive information from your account like fb_dtsg token, same thing for oAuth Dialogs I can set my iframe source to to it and steal Your access_token :)
I hope it's clear enough why Facebook uses
header('X-Frame-Options: DENY');

How can I get oauth token and oauth token secret using the ruby omniauth-twitter gem?

I setup my app similar to the tutorial here - http://railscasts.com/episodes/235-devise-and-omniauth-revised. If you cant access it, below is my code
Omniauth controller callback
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :twitter, :all
end
user model
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
user.name = auth.info.name
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"], without_protection: true) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
Now I am wondering how do I obtain the authenticated user's oauth token and oauth token secret?
Thanks
If you want to take a look at the information returned by a certain provider, put this as the first line of your callback controller:
raise env["omniauth.auth"].to_yaml
You'll be able to see that the information you want can be accessed in auth.credentials.token and auth.credentials.secret.
EDIT: Now that Rails 4 uses the better_errors gem, this method of inspecting the omniauth hash no longer works so well. A better way now is:
render :text => "<pre>" + env["omniauth.auth"].to_yaml and return

Rspec does not seem to be targeting the correct controller

Getting really bizarre rspec behavior in one of my controller specs.
It's best to illustrate. In rubymine, when I set a breakpoint, this happens:
#rspec test
describe Api::V1::UsersController do
let(:user) { FactoryGirl.create(:user) }
describe "#show" do
it "responds successfully" do
get 'show', id: user.id
response.should be_success
end
end
#controller
class Api::V1::UsersController < AuthenticatedController
def show # !!! RubyMine breakpoint will stop execution here !!!
user = User.find(params[:id])
user_hash = User.information(user, current_user)
respond_to do |format|
format.json { render json: user_hash.to_json }
end
end
So the above works as expected.
But, now this test fails.
#rspec test
describe UsersController do
let(:user) { FactoryGirl.create(:user, is_admin: false) }
describe "#show" do
it "redirects non-admin" do
get 'index'
response.should redirect_to user_path(user)
end
end
#controller
class UsersController < AuthenticatedController
def index # !!! Breakpoint is never hit !!!
#users = User.all
respond_to do |format|
if current_user.is_admin
format.html
format.json { render json: #users }
else
redirect_to user_path(current_user) and return
end
end
end
By the way, this is the result:
Expected response to be a redirect to <http://test.host/users/625> but was a redirect to <https://test.host/users>
None of my breakpoints in controller methods in UsersController are hit. BUT all controller methods are hit if I set breakpoints in API::V1::UsersController.
Any guidance is greatly appreciated. I'm really at a loss of how to debug this.
Sorry, this question was more out of frustation than anything. But I finally figured out what was going on. Hint: tailing the test.log is a good idea.
I was forcing ssl on the controller. The request rspec sent is http. ActionController::ForceSSL redirects the request to https and to the same controller#action. However, at this point, the rspec test was finished and failed the test because it only sees the redirection back to the same controller#action.
So in a before(:each) or something similar, use this: request.env['HTTPS'] = 'on'. All tests work as expected now.
I think maybe you're stepping outside of the domain of rspec here with regards to redirect testing. May I suggest using capybara and rspec?
My sources:
Rspec - Rails - How to follow a redirect
http://robots.thoughtbot.com/post/33771089985/rspec-integration-tests-with-capybara

rails 3 redirect_to pass params to a named route

I am not finding much info on how to do this even though there are lots of suggestions on how to pass params to a redirect using hashs like this redirect_to
:action => 'something', :controller => 'something'
in my app I have the following in the routes file
match 'profile' => 'User#show'
my show action loos like this
def show
#user = User.find(params[:user])
#title = #user.first_name
end
the redirect happens in the same user controller like this
def register
#title = "Registration"
#user = User.new(params[:user])
if #user.save
redirect_to '/profile'
end
end
The question is in the register action when I redirect_to how do I pass along the params so I can grab that user from the database or better yet ... I already have a user variable so how do I pass along the user object to the show action?
-matthew
If you're doing a redirect, Rails will actually send a 302 Moved response with a URL to the browser and the browser will send another request to that URL. So you cannot "pass the user object" as in Ruby, you can only pass some url encoded parameters.
In this case you would probably want to change your routing definition to:
match 'profile/:id' => 'User#show'
and then redirect like this:
redirect_to "/profile/#{#user.id}"
First off, I'd name your route, to make using it easier:
match '/profile/:id' => 'users#show', :as => :profile
You would then redirect to it, like so:
redirect_to profile_path(#user) # might have to use profile_path(:id => #user.id)
Then to pull the user from the database:
def show
#user = User.find(params[:id]) # :id comes from the route '/profile/:id'
...
end
As an aside, if you use something like Devise for authentication, it provides you with a current_user method, and therefore you wont need to pass around the user's id:
match '/profile' => 'users#show', :as => :profile
redirect_to profile_path
def show
#user = current_user
end

Resources