Have CanCan use Active Admin user? - ruby-on-rails-3

I'm using Devise and CanCan for authorization on the frontend of a Rails 3 app. I also have Active Admin as the interface for the backend. I'm trying to create different roles for admins in the backend. Both ends have a login form that uses different 'user' models & tables. The problem is that CanCan fetches the current user from the frontend (grabbing the current user object) and uses that to see if someone in the backend has the correct permissions.
So, how I can have CanCan correctly grab the admin user that's logged in?
If anyone needs more information, I'll be glad to supply it.

I have not used ActiveAdmin before, but have used Devise and Cancan in a couple of projects before.
Having looked at Active Admin Documentation,
Set the method that controllers should call to authenticate the current user with:
# config/initializers/active_admin.rb
config.authentication_method = :authenticate_admin_user!
Set the method to call within the view to access the current admin user
# config/initializers/active_admin.rb
config.current_user_method = :current_admin_user
You can override Cancan behaviour in your application, by looking at :current_admin_user instead of :current_user.
Refer here Cancan changing defaults.
If you still can't get it, post your problems, where you are stuck.

Related

Cancan + Devise, best way to have admin manage users

I am working on an app where a company admin should be able to create and update users in his company. (Users can also create and update their accounts independently).
I used Devise and Cancan for registration and permission management. I configured Devise to get the required signup and user update processes. I created a namespace for the admin views.
My admin controller (/app/controllers/admin/base_controller.rb) looks like this:
class Admin::BaseController < ApplicationController
authorize_resource :class => false
layout 'admin'
def dashboard
end
end
In addition to my "regular" users_controller, I have a controller (/app/controllers/admin/users_controller.rb) and associated views dedicated to Admin user management.
class Admin::UsersController < Admin::BaseController
...
end
Now, what's the cleanest way to implement Devise-related user admin features (at this point, create and update users)?
Have a conditional (based on user permissions) in my "regular" registrations_controller and confirmations_controller to render and redirect different views? (cf. Devise form within a different controller)
Or create new controllers in my admin namespace? If the latest is better, what are the main steps to follow?
Herein lies your issue, once you namespace or move any user management to another controller, you leave the scope of Devise. So at the point where you are in Admin::UsersController, Devise doesn't care about what you do, there are no 'devise-related' admin features as you stated. You can implement your Admin::UsersController as a standard RESTful controller if you wish.
In this manner, creating users through a namespaced controller, Devise will still perform actions such as confirmations. There is one small thing to keep in mind when creating and updating users this way. If you do not set a password as the admin, you will have to delete the password and password_confirmation from the params hash. To do so, the start of your create and update actions would look like so:
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
I employ this same method in many of my applications, and have yet to have it fail.
EDIT
namespace :admin do
resources :users
end

Set a password after creating an account with Omniauth (Rails + Devise)

How can I let users set passwords once they create an account through Omniauth? Omniauth creates a stub password during registration, but the user does not know what that password is, therefore cannot change it from the edit user page.
I tried to override the edit form with the instructions here: How To: Allow users to edit their account without providing a password. I was able to change the encrypted password in the db, but cannot log in with the new password, and weirdly enough, I do not see any errors in the console during log in failure.
Any ideas?
I am using Rails 3.0.7 and Devise 1.4.8. My sign-in/sign-up code is based on the standard Omniauth+Devise tutorial.
Omniauth only provides you with information on a remote user, with the guaranty that this user is connected to the provider. You then have three options :
create a user with that information, and set fields that are not specified with default method (which is what you did for password)
use that information to prefill devise sign up form (self.new_with_session as described in your tutorial) and let users create their account themselves, filling in the missing fields (such as password)
store the omniauth information in a session (or cookie), redirect your user to a form with the missing fields, and use both form information and session information to create your new user.

Devise user management

I'm creating a Rails app using Devise for user management (Rails 3.0.10, Devise 1.4.2). I've got the basics going - signup, login / logout - but I can't seem to find any facility (or even documentation surrounding) user management.
In other Rails apps I've created, I've had a UsersController with an index method that allows an administrator to list all the users of a system, & to subsequently modify them through edit and update actions on the same controller.
I can't see an obvious way of doing this with Devise. I've used the Rake tasks that come with Devise to generate editable views, which is great, but I can't figure out how to do the equivalent with the Controller.
I fear I'm missing something fundamental here. Could someone please point me in the right direction?
Devise doesn't come with any sort of Admin interface. If you are the only administrator and don't mind a little crudeness - there is always the console and/or scaffolding.
There are also a lot of good gems that make setting up admin interfaces a cinch: Active Admin, Rails Admin and I'm sure there are a bunch more out there.

Testing access control with cucumber & capybara

I'm using rails_admin and devise in my rails 3 application and writing tests for access control in cucumber.
I want to test that someone not admin cannot access all the routes of rails_admin (for example)
If I explicitly test like so:
Scenario: An authenticated user cannot access site administration
Given I am an authenticated user "kate", "kate#example.com"
When I visit the administration page
Then I should see access denied
I can match the string "the administration page" to the route "rails_admin_dashboard_path" and make my test pass just fine. But this seems to be testing the application the wrong way around. I want to test ALL the routes (loop through them somehow) rather than imply them and maybe miss one or two.
Something like this:
Scenario: An authenticated user cannot access site administration
Given I am an authenticated user "kate", "kate#example.com"
When I visit ANY administration page
Then I should see access denied
Could anyone advise me on how to effectively test this? Am I taking the right approach? Should I be doing this in rspec instead?
As you might be able to tell, I am a bit of a n00b.
I don't think you should be aiming to test every possible route in your Cucumber scenarios. As Andrea S. suggests, if all your admin controllers have a common base, then it should be sufficient to check the admin home page.
One approach would be to create a base controller in your admin namespace that all other admin/controllers inherit from. You can put a before filter in that base controller to check for admin authentication. Like so:
#app/controllers/admin/base.rb
class Admin::Base < ApplicationController
before_filter :ensure_admin_logged_in
end
And have all your other controllers in the admin namespace inherit from this one:
#app/controllers/admin/Pages.rb
class Admin::PagesController < Admin::Base
layout "admin"
end

Previous user picked up with auth_token login in Devise

Here's my scenario.
First, I log in as Alice:
http://localhost:3000/?auth_token=eMXBk8cuJMA1ETZfMIgB
Then, without logging out, I log in as Bob:
http://localhost:3000?auth_token=Z9Ui7Cw_xCnOmGWOEUEH
What happens is, after the second GET request, I'm still logged in as Alice, not Bob.
If I do a http://localhost:3000/users/sign_out in between the two auth_token logins, everything's OK.
Without the sign_out, Bob can't login using his token.
Is this a bug, or the way things should be due to some security issues I'm ignorant of?
Can this behavior be overriden through hooks?
I've run into this with restful_authentication and devise. Here is what I use to handle it (put in application_controller.rb) and call it where needed. Note that I use :ak for the auth token. Change to whatever you're using.
def switch_session(api_key_passed)
if api_key_passed != current_user.authentication_token
logger.info("******Switching session as token is different.*******")
user = User.find_by_authentication_token(api_key_passed)
sign_out(user)
if #api_login_enabled.present?
redirect_to(new_user_session_path(:ak => api_key_passed))
else
logger.info("***API Login Setting is Disabled.***")
end
end
end
Devise's token_authenticatable strategy is a login path. Sending a User's authentication_token to Devise will log in that user and set a session, just as logging in via the web would. It is not supposed to act as an API Key, which would be required to be sent on every request and knowledge of that request disappears once the server responds.
Take a look at this issue here for more information: https://github.com/plataformatec/devise/issues/300
#jschorr's answer will work if you wish to use it more like an API key, but you should be aware that the original issue will not actually persist the previous user's session between different clients, this is not a security issue of sessions leaking between clients, and this is exactly how the authors of Devise intended. Just as you would need to log out of your Significant Other's webmail account in order to check your own if they just checked their mail from the same computer, you would need to send a logout message to your Rails app before you can switch accounts.
You are missing a setting on devise.rb initializer:
# By default Devise will store the user in session. You can skip storage for
# :http_auth and :token_auth by adding those symbols to the array below.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing :skip => :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:token_auth]
So no session is used when a user authenticates with an auth_token.