In CanCan, an ability is assigned to an individual user. Is there a way to access that user when you have an ability record.
I'd like to be able to say:
#ability = Ability.new(User.find(3))
#ability.based_on_user # Does not exist, but would respond with the same thing as User.find(3)
In my app, users can impersonate other users, which involves resetting #current_ability = Ability.new(#impersonated_user) and I'd like to be able to quickly get that user for debugging purposes. The documentation doesn't give any such method, but I would be okay with a hack, since it's just for my own purposes in testing.
Just add that method yourself to your ability model. Something like this:
# in app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
#user = user
# the rest of your code
end
def based_on_user
#user
end
end
Related
I need help to configure CanCanCan using ActiveAdmin. I have everything else working including devise. I can restrict menus using devise but if you know the URL lets say for edit you can still edit that resource. I want to restrict a normal user from editing/creating any resources but it does not seem to work.
Active_Admin.rb
config.cancan_ability_class = ActiveAdmin::CanCanAdapter
Ability.rb (simple out of the box)
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
#
# user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, Student
else
can :read, Student
end
end
end
User model.
admin:boolean
and if I login with a user who is not an admin i can still create/edit/delete, I just want to restrict them to read only.
Please help i am struggling with this only feature that I need to complete.
Thanks in advance
Change this thinks:
config.authorization_adapter = ActiveAdmin::CanCanAdapter
config.cancan_ability_class = Ability
authorization_adapter tells active admin which adapter it should be use.
cancan_ability_class tells the adapter which class it should use.
If that still doesn't work, try to rename Ability to AdminAbility.
I know there's probably solutions to this elsewhere, but I'm looking for help that works specifically in my case because I'm having a lot of trouble translating other solutions into my situation.
I currently have a device set up and the database is seeded so an admin is already created. Everyone else that signs up after that is a user.
There are two tables right now, a user table generated by rails and a cadet table. The cadet table stores information such as company, room number, class year and such.
My question is, how do I allow a user to edit/destroy only the cadet record that they've created? I know it seems like a big question but I've been looking all over and still can't find a reasonable way to implement this. Thank you!
Devise is related to authentication (who you are), you need a solution for authorization (who can do what). My suggestion is to go for CanCan (https://github.com/ryanb/cancan), which is a gem very widely use together wide Devise.
For your example, and after install the gem via Gemfile+Bundler:
Initialize the gem for your User model
rails g cancan:ability
it will create a file in app/models/ability.rb to define your restrictions
Define your restrictions, for instance:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (this line it to manage users not logged in yet)
if user
can :manage, Cadet, user_id: user.id
end
end
end
That will allow a user just to read, create, edit and destroy Cadets which user_id matches the id for the User.
Take a look at CanCan github page is wery well documented and with lot of examples; it's very simple to set up and works great.
You can also use a before_filter, something like the following:
class CadetsController < ApplicationController
before_filter :cadet_belongs_to_user, only: [:show, :edit, :update, :destroy]
....
private
def cadet_belongs_to_user
# following will work only on routes with an ID param
# but there are a few ways you could check if the cadet
# belongs to the current user
unless current_user && current_user.cadets.where(id: params[:id]).any?
flash[:notice] = "You are not authorized to view this page."
redirect_to root_path
end
end
end
In my rails application, my user is able to login once and switch between accounts as they please. When they do, I need to reinitialize their abilities since the permissions are set at the account level in the database.
My initial thought is to initialize the ability class when I detect a change in the account but not sure how to accomplish this.
I am of course open to any other idea. I am new to CanCan.
The Ability class is where all user permissions are defined.
Example:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
The 'can' method is used to define permissions and requires two arguments. The first one is the action you're setting the permission for, the second one is the class of object you're setting it on.
I use devise for authentication and only an admin can create a user.
I use cancan to assign roles to the user during user creation.
I want the admin to view all the users roles and the admin should be able to edit the roles of the users.
How can I do this?
Check out https://github.com/marklocklear/devise_multi_tentant. In ability.rb I have...
class Ability
include CanCan::Ability
def initialize(user)
can :manage, :all if user.role == "admin"
end
end
You can add a role attributes for User model.
Then, when user go to admin dashboard, you can check user's role. If user isn't admin, redirect to another page (using before_filter :function)
But, instead doing everything manually, you can use gem cancan instead, it's so popular and easy to use!
You can find the document here: GitHub
Or in rails cast episodes 192
Basically, it'll create a ability model, and you will declare privilege for user, a sample ability class
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :update, User
end
end
end
After that, you can authorize for user in action, controller or views, like the example see this link
PS: sorry if my English was too bad, thanks:)
I was wondering how I can define an ability class and serve that ability class depending on the user that has logged in.
I am using Active Admin, Can Can and Devise and I have successfully created a User and an AdminUser models.
I have this in my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if (user)
can :manage, Item
end
end
end
Now I have used this wiki entry to determine that we can indeed define a custom ability file and use that instead of the ability.rb:
https://github.com/ryanb/cancan/wiki/changing-defaults
But what I wanted to do is, be able to use ability.rb if a "non-admin user" is signed in and a custom abilty if a user admin is signed in.
Side Question: Could it be done such that I don't need a custom one and I could set permissions in one ability.rb file?
I've never really used ActiveAdmin, so I'm not entirely sure if I'm missing something, but it doesn't seem like that framework relies on CanCan. That's why I'm assuming you're defining a current_ability method like explained in the wiki and it's instantiated with Ability.new(current_user).
If that's the case, and your current_user can be either a User or an AdminUser, then there's no problem in checking for that in the Ability class:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.kind_of? AdminUser
can :manage, Item
elsif user.kind_of? User
can :read, Item
end
end
end
You can simply take a look at the user's type and change the rules accordingly. You can also use is_a? instead of kind_of? for stricter checking, but it's probably not required and might cause issues if you decide to do inheritance later on.
Another way you could check is by defining an admin? method in both models. This might be a better way to do it, since explicit type checking is not very popular in ruby -- it often limits your choices. It might look like this:
class User < ActiveRecord::Base
def admin?
false
end
end
class AdminUser < ActiveRecord::Base
def admin?
true
end
end
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.admin?
can :manage, Item
else
can :read, Item
end
end
end