Session in Action Mailer - how to pass it? - ruby-on-rails

Let's say I have a website where people can get a free ebook if they will sign up for a newsletter - after they've done it, I will create a User model and I will show them Edit Form to add some extra details about them.
I don't want to force them to add a password or any other details on the first page because it would decrease conversions and I don't require the additional information either. Also, I don't want them to have forever access to the Edit page so I solved it by assigned a session to them and recognize it through it on the Edit page. This is my controller:
class UsersController < ApplicationController
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
UserWorker.perform_in(5.minutes, 'new_user', user.id)
redirect to edit form...
end
end
def edit
#user = User.find(session[:user_id])
end
def update
#user = User.find(session[:user_id])
#user.update!(user_edit_params)
redirect_to user_thank_you_path
end
end
But if they won't add extra information within 10 mins, I will send them an email via ActiveMailer with a link to the Edit form and ask them to do so.
Th question is how could I identify the user through the session and show them the form - how could I do User.find(session[:user_id] via ActionMailer)? Is it actually a correct way or would you recommend a different approach?

One way could be to set a background job to run in 10 minutes.
Inside that job, you would check if they're still "unregistered". You deliver the email if they've yet to complete the registration.
Something like this;
class UsersController < ApplicationController
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
RegistrationCompletionReminderWorker.perform_in(10.minutes, user.id)
# redirect to edit form...
end
end
end
class RegistrationCompletionReminderWorker
def perform(user_id)
user = User.find(user_id)
if user.password.nil? # or whatever your logic for registration completion is
UserMailer.registration_reminder(user_id).deliver_now
end
end
end

Related

How can I make specific user(not current_user) sign out on rails-devise website?

I want to know how I can make specific user(not current_user) sign out.
I saw this http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/SignInOut#sign_out-instance_method and maked this code.
def kick_admin
user = User.find params[:user_id]
user.admin = false
user.save
sign_out user #want to kick him.
end
But it does not make that user sign out but make me(current_user) signed out.
What is the right way to use the sign_out method?
I checked this answer(Sign out specific user with Devise in Rails) but it was not helpful.
One way you could do this is create a new attribute in the User table, call it force_sign_out.
def kick_admin
user = User.find params[:user_id]
user.update_attributes(admin: false, force_sign_out: true)
end
And have a before action in ApplicatonController so that if the user attempts any activity he's signed out
class ApplicationController < ActionController::Base
before_action :check_if_force_sign_out
def check_if_force_sign_out
return unless current_user.force_sign_out
current_user.update_attributes(force_sign_out: false) # reset for non-admin log in
sign_out
redirect_to root_path
end
end

Rails 4: Profile for users

I have a Rails 4 app with a User model and it has_one :client where Client is the model where I store information about the client. I want my users to be able to view a profile page with their client information but I'm not sure which action to use or how to route to it.
I want to be able to view a particular client using the show action:
def show
#user = User.find(params[:id])
#client = #user.client
end
...but I also want a route for "my profile" which would rely on current_user and not finding a user by their ID. In my mind this is also a show action and I'm trying to avoid adding custom methods and would rather find the "railsy" way to do this.
def show
#user = current_user
#client = #user.client
end
I know this is sorta philosophical, but if someone could explain the proper way to achieve this I would appreciate it.
Actually, I am not sure what you are trying to do. If you want to add a specific page about showing current_user profile. Why not adding a new method?
Like this
def profile
#client = current_user.client
end
Or maybe you can validate the params[:id] in show action
Like this
def show
#user = params[:id].present? ? User.find(params[:id]) : current_user
#client = #user.client
end

Authorize related object when using pundit gem

If I have user object and user has one basicinfo. In user show action I have:
def show
#user = User.find params[:id]
authorize #user
end
And in show.html.erb I must show user's basicinfo, such as:
User name is: <%= #user.basicinfo.name %>
In this case should I also authorize basicinfo in user show action?
def show
#user = User.find params[:id]
authorize #user
authorize #user.basicinfo, :show?
end
The authorization applies to the entire action.
If you want to filter out some elements in the view you can do so on an ad hoc basis, basically applying whatever attribute you are using in the xxxPolicy class (which is not provided above)
Handling user authorization is possibly too complicated via Pundit
def initialize(user, user)
I definitely do filtering in the views when it comes to user actions

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?

Rails Security: redirect if not current_user

I've been using authlogic and it works really well. One thing I've noticed is that as a hacker I could easily type in this address:
localhost::3000/users/a_user_that_is_not_me/edit
Since the form in the edit form is for #user which is set to current_user and requires an authenticity token, even if I tried to put in details for the other user I end up changing my own account instead of the other users.
That's nice and good, but I'd like it so that these hackers get redirected before they even see the form.
I tried this in the users_controller:
def edit
if admin?
#user = params[:user]
elsif User.find_by_username(params[:id]) != current_user
#user = current_user
#not_user = User.find_by_username(params[:id])
redirect_to user_path(#not_user)
else
#user = current_user
end
end
The redirect works if I type in an address with another user's name but I get a 404 error when trying to access the edit page for the current user.
Any ideas why this doesn't work?
If you're going to be doing this kind of thing a lot, check out an authorization plugin like authorization-san.
Authorization differs from authentication in that authentication is logging in, but authorization pertains to the authenticated (or un-authenticated) user's rights to perform actions.
With authentication-san, you could define this rule with this piece of code in your controller:
# this assumes you've got some way to set #user to the user you're looking up,
# e.g. in a before_filter
allow_access(:authenticated, :only => [:edit, :update]) { current_user == #user }
It looks like you are assigning #user to a string if the current user is an admin. This is simpler (less typo-prone):
def edit
u = User.find_by_username!(params[:id])
if admin? or current_user.username == params[:id]
#user = u
else
redirect_to user_path(u)
end
end
Also, don't you want to use find_by_username! (with bang on end) so that a 404 page is rendered when the user is not found? I'm not sure how you're getting the 404 page now...

Resources