Cancan is not working well with active_admin - ruby-on-rails-3

I have implemented the cancan with active_admin using below link.
https://github.com/gregbell/active_admin/wiki/How-to-work-with-cancan
Just in my case the only change is below written code.
app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= AdminUser.new # guest user (not logged in)
if user.id == 1
can :manage, :all
puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> manage all"
else
can :read, :all
puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> read all"
end
end
end
For now just put condition on user.id.
so when i run my application i can see my puts are on right login.
Question:
so if i login with user whos user.id != 1 then also i can manage all my modules in active_admin.
cancan ability not working for me.
(In short my can :code isn't working in any condition)
Using rails 3.1.1, cancan 1.6.7, activeadmin 0.4.4, ruby 1.9.3
Followed commands in link correctly, double checked.
Used authorize_resource in AdminUser.
Using socery not devise, does this thing affecting the cancan?

I write the following in code in every model in /admin
then my conditions in ability model start working.
menu :if => proc{ can?(:manage, #ModelName) }
controller.authorize_resource
Before posting this question i just written the above code only in admin users

Related

ActiveAdmin admin_users table devise users table logging in issue

Following is my setup
devise (4.4.3)
activeadmin (1.3.0)
Rails 5.2
I created a user table with devise. installed active admin and used the normal method and created admin_users table. NO I AM NOT USING THE SAME TABLE. THESE ARE DIFFERENT TABLES (I had to do this cause last time this question was marked as duplicate cause the user thought i used the same table)
Routes file looks like this:
devise_for :users
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
devise_scope :user do
authenticated :user do
root 'home#dashboard', as: :authenticated_root
end
unauthenticated do
root 'devise/sessions#new', as: :unauthenticated_root
end
end
Now the issue is root_path '/' redirects correctly and I am able to login as User. But I am not able to login as Admin. It redirects me back to root_path that is unauthenticated user.
But weirdly when I login as user, I can then login as admin_user.
AGAIN I AM USING DIFFERENT TABLES, admin_users and users
Thanks in advance.
I can tell you an easy way to add admin in devise
$rails g migration add_admin_to_user
in the db
def change
add_column :users, :admin , :boolean , {default: false}
end
in user.rb
def admin?
admin
end
<% if current_user && current_user.admin? %>
<% end %>
then i do sign up from the email that i want to be the admin
then in rails c select the email that you want it to be the admin for example
User.last
user=User.last
user.admin=true
user
user.save
Solved this.
So the issue is when you have activeadmin admin_users and users table that is created with devise, and you place the authenticated_user! before_action in application_controller, activeadmin interprets it as authorization failure(though this should not be the case).
Solution to this is, go to active_admin.rb and add the following code
config.skip_before_action :authenticate_user!
This forces activeadmin to listen to only its authrization_adapter.

How to implement the dynamic authorization with CanCan?

i am following this tutorial http://eveloverails.wordpress.com/2011/04/02/183/. I have created the users through devise and i want when the user log in as an admin he should have the list of users in which it can edit the permissions of the users like edit destroy,etc.
Thanks in advance
you can add a field in devise model call role. then create cancan ability class in your model then define your authorization like
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
user ||= Login.new # guest user (not logged in)
if user.role == 'admin'
can :manage, :all
else
can :read, :all
end
end
end
See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities

Where to override current_user helper method of devise gem

How can i override current_user of devise gem.
Actually I need to add web services for mobile-app.
Currently devise is managing session and 'current_user' for web-application.
Now Mobile app will send user_id to the server. I need to override current user like this
def current_user
if params[:user_id].blank?
current_user
else
User.find(params[:user_id])
end
end
Should I need to modify devise gem as plugin ? or something else ?
Kindly explain in detail as I am new in rails.
Kind regards,
According to the module Devise::Controllers::Helpers, current_user (together with all other devise helpers) is added to ApplicationController, which means that you can override it in this way:
# in application_controller.rb
def devise_current_user
#devise_current_user ||= warden.authenticate(scope: :user)
end
def current_user
if params[:user_id].blank?
devise_current_user
else
User.find(params[:user_id])
end
end
The other answer suggesting aliasing the method is actually not the best solution. Doug English's comment is the best solution:
# In ApplicationHelper
def devise_current_user
#devise_current_user ||= warden.authenticate(:scope => :user)
end
Here's the reason:
Suppose you're including your ApplicationHelper in your controller. If you need a method in your ApplicationHelper that relies on devise_current_user, given the alias solution inside the controller, you're out of luck.
But with the explicit method definition above, you can move the definition to the helper and call it from other methods and you still get to include it in the controller.
This is useful, for example, when you're developing a user impersonation solution and you need to show two different users in the view, one for the real admin (devise_current_user) and the other, the impersonated user (current_user).
Limbo-Peng's answer is great, but can be improved a little to make sure only admins can do this:
You'll need to also define a is_admin? method or is_admin attribute on the User class.
You may also want to use a different key than user_id, so it will never conflict with your regular parameters.
# to impersonate another user, e.g. for customer support
# only admins can do this..
#
alias_method :devise_current_user, :current_user
def current_user
if ! params[:user_id].blank? \
&& devise_current_user && devise_current_user.is_admin?
User.find(params[:user_id])
else
devise_current_user
end
end
Assuming we can trust our session data (which relies on whether you put user input in there without proper authorization or not), this might work as a Concern:
module Concerns
module ControllerWithImpersonation
extend ActiveSupport::Concern
included do
helper_method :devise_current_user
end
def current_user
if session[:impersonated_user_id].blank?
devise_current_user
else
User.find(session[:impersonated_user_id])
end
end
def devise_current_user
#devise_current_user ||= warden.authenticate(:scope => :user)
end
end
end
I'm using this in a project for now.
A minor question (in the answer, sorry) ... should I be aware of any changes in Devise or Warden that make devise_current_user above outdated?

Login to other user's account with Devise+Active Admin+Switch User

I'm trying to implement switch_user gem in my existing rails 3.0.9 application.
There are two models on my application, they are
User - for my customer accounts and it has_one Account
AdminUser - This was created by ActiveAdmin
I have already enabled devise authentication for Users and ActiveAdmin also working pretty much well with AdminUser. Now from my Active Admin interface I'd like to select the Accounts and login to those account just like the account owner does. Switch user is working fine but the problem is anyone can simply login to the user accounts now if they know the urls.
http://localhost:3000/switch_user?scope_identifier=user_1
All I need is allow only an AdminUser (i.e if there is an ActiveAdmin session) to access the User's accounts.
This is how my /config/initializers/switch_user.rb looks like
SwitchUser.setup do |config|
config.controller_guard = lambda { |current_user, request| current_admin_user.nil?}
config.redirect_path = lambda { |request, params| "/dashboard" }
end
But I get this error
NameError in SwitchUserController#set_current_user
undefined local variable or method `current_admin_user' for main:Object
Is there anyway I can access the active admin session?
Code for /config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
config.site_title = "MyAppName"
config.authentication_method = :authenticate_admin_user!
config.current_user_method = :current_admin_user
end
btw in my application controller I haven't created any methods for authenticate_admin_user , current_admin_user active admin works fine without them.
You need modify local config/initializers/switch_user.rb:
config.controller_guard = lambda { |current_user, request, original_user, controller|
controller.admin_user_signed_in?
}
Original lambda has 2 arguments.
Just append more (up to 4) and use it.
Don't forget restart rails server :)
OK I think I found a solution to secure the switch_user. All I did is moving the routes inside the admin_users scope
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config do
match '/admin/switch_user', :controller => 'switch_user', :action => 'set_current_user'
end

Howto redirect users based on their roles in rails 3?

I am trying to figure out how to redirect users on certain URL based on their role, after they log to the Ruby/Rails3 application.
So far, I have used authlogic gem for authentification and cancan gem for role setting.
Roles are just like this (defined in app/models/user.rb):
class User < ActiveRecord::Base
acts_as_authentic
ROLES = %w[admin customer demo]
end
Now there is app/controllers/user_session_controller.rb which is taking care of logins.
I would like to make something like this:
for r in User.role
if r == "admin"
redirect_to admins_url
else
redirect_to users_url
end
end
This is not working because of the following error:
"undefined method `role' for #<Class:0xb5bb6e88>"
Is there a simple or elegant way how to redirect users to the certain URLs according to their roles?
(Roles are defined in mysql column 'role' in the users table.)
The for r in User.role is confusing. Are you trying to access the array of ROLES defined on the class or are you trying to access the role value of the current user?
If you are trying to access the array of ROLES, then use User::ROLES.
Using authlogic, one typically defines the current_user in the application_controller. So the role of the current user can be found using current_user.role
So your code could look something like
if current_user.role == "admin"
redirect_to admins_url
else
redirect_to users_url
end
You should definitely check out CanCan. It is a pretty logical way to manage user roles and abilities.