REST API: Custom method using cookies and sessions - ruby-on-rails

I am trying to use REST API, so I want get a #current_user in APP2 from a RoR APP1.
In APP1/config/routes.rb I have this code:
resources :users do
collection do
get 'current'
end
end
In APP1/controllers/application_controller.rb I have this code:
before_filter :current_user
def current_user
if cookies[:remember_me]
current_user = user_from_cookie
else
current_user = User.find_by_id(session[:current_user_id])
end
unless !current_user.nil?
default_current_user = User.find_by_id(1)
end
return #current_user = current_user.nil? ? default_current_user : current_user
end
In APP1/controllers/users_controller.rb I have this code:
def index
...
end
def show
...
end
...
def current
respond_to do |format|
format.xml { render :xml => #current_user }
end
end
In APP2/models/user.rb I have this code:
class User < ActiveResource::Base
self.site = "http://APP1"
end
In APP2/controllers/application_controller.rb I have this code:
before_filter :current_profile
def current_profile
#current_profile = User.get(:current)
end
Now, if I Sign in my User2 in APP1 and I go to http://APP1/users/current.xml URL I get the correct #current_user (User2 object), but if I go to http://APP2/, even though I have 'before_filter's, the #current_profile will be always the default_current_user (User.find_by_id(1) object) instead of User2.
It seems do not care this code from APP1/controllers/application_controller.rb:
if cookies[:remember_me]
current_user = user_from_cookie
else
current_user = User.find_by_id(session[:current_user_id])
end
What is wrong?
EDITED
Maybe we can solve this problem through APP1/config/routes.rb parameters (?!):
Example: in APP1/config/routes.rb
resources :users do
collection do
get 'current', :current_user => #current_user # ?!
end
end
or something like that.

When you do a request to another website, the cookies of the current user are not accessible. The request is from server to server, so the application knows nothing about the user requesting it. I think a solution would be to send the parameters yourself and check for those.

If APP2 requests something from the APP1 via ActiveResource it is not the same as the APP1 logged in user requesting it. Users cookies are not 'forwarded'. Somebody correct me if this is nonsense. :)
Authentication part here http://api.rubyonrails.org/classes/ActiveResource/Base.html has a couple of valid options listed.

Related

Should static pages have access to cookies in rails controller and routes.rb?

I'm sure this is an obvious question but I just don't understand why this isn't working.
I'm finding that my static pages defined in the routes.rb file don't seem to have access to cookies? Is that correct? I'm trying to read the value of a cookie but the pages below seem to return a null object.
My routes.rb contains the following:
scope controller: :static do
get :about
get :terms
get :privacy
get :cookies
get :returns
get :delivery
end
For completeness here is the Static Controller:
# frozen_string_literal: true
class StaticController < ApplicationController
before_action :find_order
def index; end
def about; end
def pricing
redirect_to root_path, alert: t('.no_plans') unless Plan.without_free.exists?
end
def terms; end
def privacy; end
def cookies; end
def returns; end
def delivery; end
end
And this is how the cookie is set:
class OrdersController < ApplicationController
before_action :find_order
def add_hamper
#order ||= Order.create!(
ordernum: find_next_order_number,
user: current_user,
basket: true
)
#order.hampers << Hamper.friendly.find(params[:id])
update_order_total(#order)
if current_user&.custaddress
#order.update(custaddress: current_user.custaddress)
else
cookies[:ordernum] = #order.ordernum
end
redirect_to order_path(#order.ordernum)
# Update basket in Navbar
# Save the information as a cookie reference if they are not signed in
end
At the start of each controller I have a before_action to find an order if it exists in the DB or Cookie. For all other controllers, the find_order method is working. For the StaticController, there seems to be no access to the cookies.
Here is my find_order as defined in ApplicationController:
def find_order
#order = if current_user
Order.where(
user: current_user,
basket: true
).first
else
if cookies.dig[:ordernum]
Order.where(
ordernum: cookies[:ordernum],
basket: true
).first
end
end
end
helper_method :find_order
I've had to add the check for cookies and then if cookies[:ordernum] to stop it from failing on the static pages.
Thanks for any help with this.
PS. If anyone feels the above code could be better ... please let me know! There must be a nicer way to achieve this. It feels a little clunky.

Pundit: Ensure current_user is user from params

Apparently, you can't access the params hash in a Pundit policy. It makes sense that they want to expose as little information to the policies as possible. But one use case I'm running into, which I would think would be quite common, is to check that the current_user is the user from the params.
So here's my new action in my controller:
class ReviewsController < ApplicationController
...
def new
#user = User.friendly.find(params[:user_id])
unless current_user.admin? || current_user.id == #user.id
flash[:alert] = 'Access denied.'
redirect_to root_url
end
#review = #user.reviews.build
end
...
end
So here, I'm saying to authorize if the user is an admin, or the current user is the same as the one in the URL. Otherwise, the user with the id of 2 could go to /users/1/reviews/new.
There doesn't seem to be any way to handle this in the policy, because I can't pass the params[:user_id] into the policy.
Is there a way to handle this authorization scheme from a Pundit policy, rather than handling auth logic in my controller?
Not sure if this question is out of date.
When pundit does the authorization in the controller, it will pass two objects. One is record and another is current_user. But you only need to provide the record when you call the authorize method, current_user will be passed automatically.
#authorize(record, query = nil) ⇒ true
In your case, when you call authorize(#user, :new?), in your policy, #user will be referenced as record, and current_user will be referenced as user.
Therefore, in your policy:
class UserPolicy < ApplicationPolicy
def new?
user.admin? || record == user
end
end
And you can check the policy in your controller:
class ReviewsController < ApplicationController
...
def new
#user = User.friendly.find(params[:user_id])
authorize(#user, :new?)
#review = #user.reviews.build
end
...
end

ActiveAdmin Redirect if not an admin

I would like to create a simple redirect for admin:false Users who are willing to go to /admin pages. I am using ActiveAdmin and a single User model with a admin:bool flag. I tried the following:
in my AA init file
config.authentication_method = :authenticate_admin_user!
in my App controller
def authenticate_admin_user!
redirect_to root_path unless current_user.try(:is_admin?)
end
And nothing happens.
I also tried creating a custom method like this in a new ActiveAdmin::AuthorizationAdapter but could not figure out how to use it
def authorized?(action, subject = nil)
user.admin?
end
Try this. In my case active admin provide current_admin_user not current_user
def authenticate_admin_user!
redirect_to root_path unless current_admin_user.try(:is_admin?)
end

How to us a permanent cookie in a before filter to filter for first time guests

I'm setting up a simple survey on my web page.
I want to add a before_filter so that the same person can't take the survey more than once.
My idea is to
1) create and save a remember_token to each survey when it is submitted.
2) create a cookie based on that remember token to be placed on the submitter's browser
3) Every time some visits the page, use a before filter to make sure they don't have a cookie that matches a survey in the database.
I put together the below, but for some reason, it automatically redirects to the thanks_path, regardless of whether I have a remember token?
Why does it do this? Am I using the session cookie incorrectly?
My surveys_controller is as below
before_filter :new_visitor, only: [:new, :create]
def new
#this is the survey form
#survey = Survey.new
end
def create
#this submits the survey and creates a cookie on the client's browser
#survey = Survey.new(params[:survey])
if #survey.save
cookies.permanent[:remember_token] = #survey.remember_token
redirect_to thanks_path
else
render action: "new"
end
end
def thanks
#blank page that just says, "thanks for taking the survey!"
end
def new_visitor
# if a browser has a survey cookie, redirect to thanks page
unless Survey.find_by_remember_token(cookies[:remember_token]).nil?
redirect_to thanks_path
end
end
I am creating the remember token in my Survey model.
class Survey < ActiveRecord::Base
before_save :create_remember_token
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
I think you need to test for the existence of the cookie[:remember_token] before using it as an argument to find_by_remember_token(). Only if cookies[:remember_token] is not nil and a record is found do you redirect to the thanks_page.
if cookies[:remember_token] && Survey.find_by_remember_token(cookies[:remember_token])
redirect_to thanks_page
end
unless Survey.find_by_remember_token(cookies[:remember_token]).nil?
this means if Survey not nil then redirect, i think you need to change to
unless Survey.find_by_remember_token(cookies[:remember_token])
or
if Survey.find_by_remember_token(cookies[:remember_token]).nil?

Gradual engagement, persistent guest user with Devise

I'm trying to set up gradual engagement in my utility app which people can use without registering e.g. notepad.cc and jsfiddle.net and I plan to create a guest user (with Devise) for the user when he 'writes' to the app.
I found this guide on the Devise wiki https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user which shows how to create a guest user for the duration of the browser session. What I want is for the user to continue using the same guest account in subsequent visits, until he signs up, maybe when I introduce subscription plans for more features.
How can I modify what's in the guide to make this possible?
Code in the guide linked above:
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# if user is logged in, return current_user, else return guest_user
def current_or_guest_user
if current_user
if session[:guest_user_id]
logging_in
guest_user.destroy
session[:guest_user_id] = nil
end
current_user
else
guest_user
end
end
# find guest_user object associated with the current session,
# creating one as needed
def guest_user
User.find(session[:guest_user_id].nil? ? session[:guest_user_id] = create_guest_user.id : session[:guest_user_id])
end
# called (once) when the user logs in, insert any code your application needs
# to hand off from guest_user to current_user.
def logging_in
end
private
def create_guest_user
u = User.create(:name => "guest", :email => "guest_#{Time.now.to_i}#{rand(99)}#email_address.com")
u.save(false)
u
end
end
And using it in the controller:
#thing.user = current_or_guest_user
#thing.save
After some yak-shaving I've managed to get it to work. Here's the working code:
class ApplicationController < ActionController::Base
protect_from_forgery
# if user is logged in, return current_user, else return guest_user
def current_or_guest_user
if current_user
if cookies[:uuid]
logging_in # Look at this method to see how handing over works
guest_user.destroy # Stuff have been handed over. Guest isn't needed anymore.
cookies.delete :uuid # The cookie is also irrelevant now
end
current_user
else
guest_user
end
end
# find guest_user object associated with the current session,
# creating one as needed
def guest_user
User.find_by_lazy_id(cookies[:uuid].nil? ? create_guest_user.lazy_id : cookies[:uuid])
end
# called (once) when the user logs in, insert any code your application needs
# to hand off from guest_user to current_user.
def logging_in
# What should be done here is take all that belongs to user with lazy_id matching current_user's uuid cookie... then associate them with current_user
end
private
def create_guest_user
uuid = rand(36**64).to_s(36)
temp_email = "guest_#{uuid}#email_address.com"
u = User.create(:email => temp_email, :lazy_id => uuid)
u.save(:validate => false)
cookies[:uuid] = { :value => uuid, :path => '/', :expires => 5.years.from_now }
u
end
end
I will accept another answer if you can show me a better way to do this.
The above solution works great.
Don't forget to setuphelper_method :current_or_guest_user to make the method accessible in views. Took me some time to figure out.

Resources