Merging ActiveAdmin users with existing user model - ruby-on-rails-3

I've set up ActiveAdmin early in my project and used the default admin_users model for authentication. I've since used Devise to set up a separate User model and have realized it would probably be much smarter to merge the two tables, such that an Administrator can have administrative actions both in Activeadmin and in the front end of the site. How can I configure ActiveAdmin to use the Users model with maybe a column to flag an administrator (eg. is_admin or event a permissions level to make Administrators and Moderators)?
Rails 3.1
ActiveAdmin 0.3.3
Devise 1.4.9

For a quick code block of how to do this using an existing "User" model with activeadmin, the answer is actually really easy. In the ApplicationController:
class ApplicationController < ActionController::Base
def authenticate_admin_user! #use predefined method name
redirect_to '/' and return if user_signed_in? && !current_user.is_admin?
authenticate_user!
end
def current_admin_user #use predefined method name
return nil if user_signed_in? && !current_user.is_admin?
current_user
end
end
And just use what Devise already has set up for authentication. The redirect_to is where you want to send users who ARE signed in and DO NOT have administrative privileges.

ActiveAdmin let's you define your own authentication methods. You can migrate your user tables to have an additional admin column and mark the existing admins as such in it, then set your authentication methods (as specified) in config/initializers/active_admin.rb.

Related

rails administrate with cancancan

Im using rails administrate for my application, but I want to limit access via the administrate dashboard to the resources being administered.
Im also using cancancan in the other parts of my rails app to manage access and permissions.
Has anyone managed to use cancancan within administrate, so that the administrate dashboard can use the abilities defined in cancancan, do display the resources and apply the same persmissions ?
Thanks
You can find some info about what needs to be done here: https://administrate-prototype.herokuapp.com/authorization
What is mentioned there works well for filtering collections of records, but breaks when trying to authorize individual resources. The solution is to override the find_resource method. Here is the final working code:
# app/controllers/admin/application_controller.rb
rescue_from CanCan::AccessDenied do |exception|
flash[:notice] = "Access Denied"
redirect_to admin_root_path
end
# Override find_resource, because it initially calls scoped_resource.find(param)
# which breaks since we are overriding that method as well.
def find_resource(param)
resource_class.default_scoped.find(param)
end
# Limit the scope of the given resource
def scoped_resource
super.accessible_by(current_ability)
end
# Raise an exception if the user is not permitted to access this resource
def authorize_resource(resource)
raise CanCan::AccessDenied unless show_action?(params[:action], resource)
end
# Hide links to actions if the user is not allowed to do them
def show_action?(action, resource)
# translate :show action to :read for cancan
if ["show", :show].include?(action)
action = :read
end
can? action, resource
end
This will get you started for basic resource authorization with CanCan. Further customization of field views might be needed if you need to restrict access to nested resources etc. But that should be pretty standard from that point forward. Hope this helps. :)

Multitenancy (acts_as_tenant) with Devise (users default scoped by subdomain) breaks sessions

Gems
ruby 1.9.3
rails 3.2.11
devise 2.2.3
acts_as_tenant 0.2.9
Code
All my models are scoped by a domain_id:
class User < ActiveRecord::Base
acts_as_tenant(:domain)
#...
end
Then, in my application_controller, I set the current tenant from the domain:
class ApplicationController < ActionController::Base
set_current_tenant_through_filter
before_filter :set_tenant
protect_from_forgery
#...
def set_tenant
#...
#domain = Domain.find_or_create_by_name(request.host)
set_current_tenant(#domain)
end
end
All works well for all models except for sessions: Everytime a page is loaded, it will log-out the first user who load a page with another tenant. By loading this page, it will log-out the first user who [... etc.]
Hypothesis: when Alice visits a domain, Rails loads current_tenant=alice_domain (ok). All works as expected, until Bob visits another domain, load current_tenant=bob_domain. When Alice refreshes her page, Rails still has current_tenant==bob_domain. Rails checks session: Alice does not exist with bob_domain scope, so Devise forces Alice logout. Then application_controller sets current_tenant=alice_domain... which logs-out Bob.
Dirty workaround: do not use acts_as_tenant in user model, scope users by domain myself in every controllers, then overwrite devise to scope login and registration by domain. And I'm unsure how to get Devise aware of current domain in sessions stuff. By the way, replacing acts_as_tenant by a manual default_scope in user falls in the same strange bugs. It seems very dirty to go this way.
I'm looking for a clean solution for days. I would be very grateful for any help.
Fixed, in application_controller, change
before_filter :set_tenant
to
prepend_before_filter :set_tenant
in order to default_scope everything, including User, before Devise checks the user's session.

Using before_filter to check that an admin or a user is signed in

Is it possible to use Ruby on Rails' before_filter method to check that one of multiple things is true? Specifically, I am using Devise in which I have defined a user and an admin and I would like to ensure that whoever is accessing a specific controller is one or the other. I have looked for answers in documentation and SO and haven't had any luck.
Thanks, here are my versions (if it helps):
Ruby 1.9.3
Rails 3.2.6
Devise 2.2.3
It is possible. I'm hesitant to actually place this as an answer because it doesn't get any simpler than this. I am assuming that you have an admin? method that returns true or false depending on the user's role.
before_filter :check_if_admin
protected
def check_if_admin
if signed_in?
raise 'Only admins allowed!' unless current_user.admin?
else
# or you can use the authenticate_user! devise provides to only allow signed_in users
raise 'Please sign in!'
end
end

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

cancan - "can :manage, all". I haven't been able to get access to all in rails 3 app with devise

I am using "check authorization" in the application controller so every action will require a permission. I'm starting with giving me, the superadmin :=], permissions to manage all. I thought manage all would give me access to the whole app without naming a resource.
user model:
def role?(role)
roles.include? role.to_s
end
application controller:
check_authorization
cancan's ability model:
def initialize(user)
if user.role? :superadmin
can :manage, :all
end
end
error message:
This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check.
Thank you.
As far as I am aware, you're going to need to call authorize_resource in your controller as a before filter so that this works.