How to authenticate single action with two devise? - ruby-on-rails-3

I have implemented two devise(version: 1.4.8) model in my project for example User and Admin.
Is there any option to configure authentication for single action in same controller with two devise?. example for CartsControler:
index, show -> can access either admin or user
create, update, delete -> can access admin only
Currently I have authenticated by calling below method from application_controller.rb
def authenticate_user_or_admin!
unless user_signed_in? or admin_signed_in?
redirect_to root_url , :flash => {:alert => "You need to sign in as admin/user before continuing..".html_safe }
end
end
in carts_controler.rb
class CartsControler < ApplicationController
before_filter :authenticate_user_or_admin!, :only => [:index, :show]
before_filter: :authenticate_admin!, :except => [:index, :show]
Is devise providing any default options to authenticate multiple devise models?
Is this proper ways to solve this problem? or any other better solution?

Related

ActiveAdmin and Warden route constraint

We recently updated to ActiveAdmin 1.1.0, on a site which maintains two distinct user models - Users and AdminUsers. ActiveAdmin authenticates AdminUsers, and the rest of the site authenticates Users. Both paths use Devise, like this:
devise_for :users, controllers: {sessions: :practitioner_sessions, passwords: :practitioner_passwords}
admin_devise_config = ActiveAdmin::Devise.config
admin_devise_config[:controllers][:sessions] = :sessions
devise_for :admin_users, admin_devise_config
Now, I have a Rails engine (Resque::Server) mounted which I want to restrict to admins, using a routing constraint like this in config/routes.rb:
module RouteConstraint
class Admin
def self.matches?(request)
request.env['warden'].user && request.env['warden'].user.admin?
end
end
end
mount ResqueWeb::Engine, :at => "/resque", :constraints => RouteConstraint::Admin
This used to work. However, now when an AdminUser is logged in to ActiveAdmin, request.env['warden'].user returns nil and request.env['warden'].authenticated? returns false.
Where do I go to check if a user is authenticated with ActiveAdmin in this configuration?
The "scopes" section of the Warden wiki gave me the clues I needed to rewrite two route constraints. Instead of querying the user method of the Warden object, I passed the relevant Devise scopes as arguments to the authenticated?() method. Remember that the Devise scopes can be found in routes.rb, e.g.:
devise_for :admin_users, admin_devise_config
devise_scope :admin_user do
# some stuff
end
So then constraints can be written like this:
module RouteConstraint
class SuperAdmin
def self.matches?(request)
warden = request.env['warden']
warden.authenticated?(:admin_user)
end
end
end
module RouteConstraint
class LoggedIn
def self.matches?(request)
warden = request.env['warden']
warden.authenticated?(:user) || warden.authenticated?(:admin_user)
end
end
end
Then I was able to use the constraints in the same way as before:
mount Resque::Server, :at => "/resque", :constraints => RouteConstraint::SuperAdmin
mount JobState::Engine, :at => "/job_state", :constraints => RouteConstraint::LoggedIn

Get current user in invitations controller

I am using devise_invitable gem in my application.
If a user is already signed in the application and he clicks on the Accept Invitation link then he is redirected to devise after_sign_in_path_for.
I want to change that and give the user some flash that he is signed in the application and needs to sign out before continuing.
After clicking on Accept Invitation link my control goes to Users::InvitationsController#edit. But I am not able to get current_user in Invitations Controller.
My routes and action is as:
devise_for :users, controllers: { registrations: "registrations", :invitations => 'users/invitations', sessions: "sessions", confirmations: "confirmations", passwords: "passwords" }
def edit
Rails.logger.debug current_user.inspect
if current_user.present?
Rails.logger.debug "---current user---"
else
render :edit
end
end
Can some one please suggest how I can get the current user in invitations controller.
This is the line in InvitationsController that prevents current_user from being assigned:
prepend_before_filter :require_no_authentication, :only => [:edit, :update, :destroy]
Add this to your controller and current_user will be assigned:
skip_filter :require_no_authentication, :only => :edit

Devise and Facebook Custom Routing

I am trying to create the user registration views and model on my website but I am having a small issue :
I am using devise and omniauth to get the facebook connect features working and it works,
But I want my facebook users when they sign in the first time to create their password,
That is also working, I redirect them to the filled sign up form and they only have to enter their password. But I want them to go to a second "sign_up form" named /views/registrations/new_facebook.html.erb where they can only enter their password and I will also add some other information,
I created the correct view and tested it but I have no idea how to create the correct routes to bypass Devise default
match '/facebook' => 'registrations#new', :as => 'new_facebook_user_registration'
I believe the issue is with match because that's what's not recognised,
If anyone can help me that would be great thanks,
I added my controller code for omniauth :
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash[:success] = "Welcome back"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_facebook_user_registration_url
end
end
alias_method :facebook, :all
end
How can I make the redirect_to new_facebook_user_registration_url actually work ?
devise_scope :user do
match "registrations/new_facebook" => "registrations#new_facebook"
end
That's the solution I copied in the registrations controller the new method and named it new_facebook and now everything is working as expected !
I think the issue is that you're not overriding the devise method that redirects to that path. Also according to the devise docs your routes should be set up with a "devise_for" call.
Here's the wiki page describing how to do what you are asking to do, although you may need a bit of custom logic to deal with cases that aren't facebook signups.
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-up-(registration)
Some example code from that page:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
'/an/example/path'
end
end
and the one for routes:
devise_for :users, :controllers => { :registrations => "registrations" }

Devise: Create User without Password then Require Password to Use Account?

I'm trying to layout a Rails app using Devise for authentication. I'd like to have an initial landing page where people could enter then email address. I'd like to create an account as soon as I get the email address and then let the user finalize the authentication process later. Is there some documentation that would would show how to do this?
Thanks!
I ended up solving this by using the "devise_invitable" gem. The only thing that I had to extend to get this working was to make sure that a user didn't need to be authenticated to send an invitation. Over-ride the invitations contoller and it's working great:
class InvitationsController < Devise::InvitationsController
include Devise::Controllers::InternalHelpers
skip_filter :authenticate_inviter!
skip_filter :authenticate!
def current_inviter
#current_inviter ||= User.new(params[:user])
end
end
I'm not sure if there is good documentation for how to do this, but it would not be hard. Just don't require authentication on your landing page, or on the post from the login form on that page. Collect an email address in the login form. Send the user a mail to the address they log in with. In the email, include the 'forgot password' link (renamed to 'click here to sign in' ... or whatever) to force the user to login and choose a password. Does that work for you, or did i miss something?
I realize this is late, but this might be helpful to others. I had a similar requirement as you. Basically, I wanted to have a user enter an email address, and persist the user as a guest. Once the user is promoted to a 'regular' user, I re-enable password authentication. First, I created a warden strategy based on the devise-gullible gem. I made a modification to the authenticate method:
class Guest < Authenticatable
def authenticate!
resource = mapping.to.find_for_database_authentication(authentication_hash)
if resource
if resource.respond_to?(:guest?) and resource.guest?
success!(resource)
else
fail(:regular_user)
end
else
fail(:invalid)
end
end
end
I define a guest user in my user model as follows:
def guest?
self.roles.length == 0
end
I'm using CanCan with devise and a HABTM to handle the roles. Currently, I have a 'regular' user and an 'admin' user. A user is a guest if he has no assigned roles yet.
Then, you need to override the devise registrations controller:
class Users::RegistrationsController < Devise::RegistrationsController
prepend_before_filter :allow_params_authentication!, :only => :create
def create
resource = warden.authenticate(:guest,
{ scope: resource_name,
recall: "#{controller_path}#new" })
if resource
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
elsif warden.winning_strategy.message == :regular_user
set_flash_message :notice, :regular_user if is_navigational_format?
redirect_to new_session_path(resource_name)
else
super
end
end
end
Note that the idea is that we attempt to authenticate a user running the guest strategy only. If he is already registered as a guest, just sign him in normally. If the guest strategy fails because he is registered, but is now a regular user, redirect to the normal sign in page.
Now it is possible to persist some limited information I may wish to collect from a guest, and not require him to have to do a full sign up. I use a normal User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :omniauthable,
:token_authenticatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
has_and_belongs_to_many :roles
def self.new_with_session(params, session)
super.tap do |user|
if user.password.blank?
user.password = Devise.friendly_token[0,31]
# Also, we don't need to do email confirmation in this case.
# The user will have the guest role, and we'll do a confirmation
# when we promote him to a 'regular' user.
user.skip_confirmation!
end
end
end
def has_role?(role)
!!self.roles.find_by_name(role.to_s.camelize)
end
end
Note that I auto-generate a password in build_with_session. You can send a auth token at the time of your choosing, and then require the user to set a new password at that time. You'll also want to then change his role so that he becomes a regular user (or do whatever it is you want to note he is no longer a guest).
Here's my partial that appears on the front page:
<%= simple_form_for(resource,
as: resource_name,
url: user_registration_path,
defaults: { required: false },
html: { class: 'well' },
wrapper: :bootstrap,
validate: true) do |f| %>
<%= f.error_notification %>
<fieldset>
<legend>New here? Let's get started!</legend>
<%= f.input :email, placeholder: 'user#example.com', validate: { uniqueness: false } %>
<%= f.button :submit, "Get my quote!", class: 'btn-primary' %>
</fieldset>
<% end %>
So, this form functions as both a guest registration and login form. The only think I don't really care for so far is the the registrations controller is handling an authentication, but I didn't see an immediate way around this.
I implemented this strategy in my local lib directory. Be sure to set your load path appropriately, and add require 'devise_guest' to config/initializers/devise.rb.

How to create a profile after user registration with Rails3 & Devise

I'm doing a simple user with profile application. User registers and
are automatically logged in. Works fine so far. Now, I'd like to
create a profile after a successful registration and redirect the user
to his/her profile.
I have a User model and controller. Devise also created the
registration controller. I installed the gem. I copied over the devise
files and I plan to override the create action.
First, whatever I edit in registrations_controller.rb nothing
changes.
class Devise::RegistrationsController < ApplicationController
prepend_before_filter :require_no_authentication, :only =>
[ :new, :create, :cancel ]
prepend_before_filter :authenticate_scope!, :only =>
[:edit, :update, :destroy]
include Devise::Controllers::InternalHelpers
Secondly, how to insert the profile creation step?
def create
build_resource
if resource.save
if resource.active?
set_flash_message :notice, :signed_up
sign_in_and_redirect(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason =>
resource.inactive_message.to_s
expire_session_data_after_sign_in!
redirect_to after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
render_with_scope :new
end
end
I was thinking to add
current_user.create_profile under is resource.active?
How would you guys tackle that issue?
First, Please format your post and use <code> blocks for the snippets. That way it becomes very readable.
Coming to your problem:
Devise by default sign ins and redirects to application root_path, after registration.
If you wish to redirect to some other path you can specify it in a couple of ways.
One is to specify root_path for your devise reource. So in your case it will be
match '/user/profile/new' => 'profiles#new', :as => 'user_root'
This will redirect you to profile#new every time you login.
To prevent redirecting to profile#new each time you can add a before_filter on profile#new to check if profile exists and redirect to some other page, say dashboards, if profile exists.
Here is the link showing how to change redirect_path for devise:
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in