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
Related
I want to add OpenID authentication in my web. To do that I have follow several tutorials and used several plugins and gems and finally I manage to do something with devise_openid_authenticatable gem. I also have normal login/password authentication, Facebook authentication and Twitter authentication. Those three work perfectly.
Now I'm trying to override Devise's Session Controller , but when I do it, the normal login/password stops working. The error I get is:
ActiveRecord::RecordNotFound in Sessions#create
Couldn't find User without an ID
The rest of the authentication forms work OK, even the sign up works perfect. It's only the login/password authentication method...
I use Rails 3.0.1, Ruby 1.8.9 and Devise 1.4.9
routes.rb
MAWeb::Application.routes.draw do
[...]
devise_for :admins
devise_for :users, :controllers => {:registrations => 'registrations', :sessions => 'sessions'}
match '/users/openid' => 'users#openid_sign_in'
match 'openid/sign_in' => 'openid#sign_in', :as => :openid_sign_in
get 'openid/create'
match '/auth/:provider/callback' => 'authentications#create'
match '/auth/failure' => 'authentications#failure'
resources :subscription_contact_datas
resources :subscription_preferences do
collection do
post :create_with_params
end
member do
get 'delete_tag'
end
get 'fill_event_id', :on => :member
end
resources :event_states
resources :subscription_profiles do
collection do
put :update_profiles
end
end
resources :event_criteria_options
resources :subscriptions do
collection do
get :options_for_event_criteria
end
end
resources :event_criterias
resources :categories
resources :user_infos do
member do
get 'edit'
end
end
resources :events
resources :users do
member do
get 'showUserActivity'
end
end
resources :admins
resources :subscriptions_from_poi
root :to => "home#index"
namespace :user do
root :to => "users#index"
end
namespace :admin do
root :to => "admins#index"
end
*registrations controller's override works perfectly
EDIT: Added the most of the routes. The ones I omited are not important in my opinion.
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?
I am using Rspec, FactoryGirl and Spork for my tests.There are 2 things I am a litte unclear on, first is the location of my factories.rb file. At present I have it located in
spec/support/factories.rb
And it looks like this
FactoryGirl.define do
factory :user do
email "example#yahoo.com"
password "password"
password_confirmation "password"
confirmed_at Time.now
end
end
Within my spec_helper I have
config.include FactoryGirl::Syntax::Methods
Secondly I want to login a user before starting my tests for a controller , this particular controller has a before filter :authenticate_user!
I am using devise for my authentication so have added
config.include Devise::TestHelpers, :type => :controller
Reading the devise docs you can add a controller_macros.rb and specify methods like so to use
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
And so i added this also to my spec_helper
config.include ControllerMacros, :type => :controller
So when I add login_user before my controller tests i get undefined method login_user. Am i using two tools here to do the same thing? Do I actually need the devise methods or can it all be done with factoryGirl. If so how do i setup the login process before i can test a controller?
Factories location should be in spec/factories. Check out this example app https://github.com/RailsApps/rails3-devise-rspec-cucumber/tree/master/spec.
For login, generally you seems to doing it right. Check the example app again and here: https://github.com/plataformatec/devise/wiki/How-To:-Controllers-and-Views-tests-with-Rails-3-%28and-rspec%29
For the undefined method login_user error be sure to have
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
and
config.extend ControllerMacros, :type => :controller
in spec_helper. Devise methods should be available wtih subject:
subject.current_user.should_not be_nil
I'm working on a Rails-based API. I recently started attempting to version it. (I'm using the Versionist gem, in case it matters) One version ('v2') uses Devise and Omniauth to authenticate users through Facebook/Twitter.
I want all the routes associated with this version to have the appropriate version prefix (so users/:username/foo becomes v2/users/:username/foo, etc.), but I've already found out that putting devise_for inside the api_version block prevents the Devise helpers (current_user, user_signed_in?, etc.) from working, so it continues to live outside the block:
routes.rb:
devise_for :user, :path => '', :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'}, :skip => [:registrations, :confirmations, :sessions, :passwords]
api_version(:module => "V2", :path=>"v2") do
resources :authentications, :only => [:update, :destroy]
devise_scope :user do
post 'login' => 'sessions#create', :as => 'user_session'
get 'logout' => 'sessions#destroy'
post 'password' => 'devise/passwords#create'
put 'password' => 'devise/passwords#update'
end
end
Everything seemed great... except the Devise-generated omniauth routes:
rake routes output:
user_omniauth_authorize /auth/:provider(.:format)
user_omniauth_callback /auth/:action/callback(.:format)
Now, some google-fu revealed that there's a devise configuration setting for this, so I added the following to our devise initializer (config/initializers/devise.rb):
Devise.setup do |config|
config.omniauth_path_prefix = 'v2/auth'
end
Now, rake routes produces paths that look sensible:
user_omniauth_authorize /v2/auth/:provider(.:format) v2/users/omniauth_callbacks#passthru {:provider=>/(?!)/}
user_omniauth_callback /v2/auth/:action/callback(.:format) v2/users/omniauth_callbacks#(?-mix:(?!))
However, when I attempt to access this route by calling api.localhost/v2/auth/facebook, I get a routing error:
ActionController::RoutingError (No route matches [GET] "/v2/auth/facebook")
Any idea what's going on here?
You are missing the provider name in the routes so they don't match the facebook part in /v2/auth/facebook. The correct route destination should look something like v2/users/omniauth_callbacks#(?-mix:facebook).
Have you specified the provider in the user model?
devise_for ..., :omniauthable, :omniauth_providers => [:facebook]
For the record, I'm using Rails 3.2 and Devise 3.0 and the altered route seems to work (I haven't gone further yet to see if something else will break).
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" }