I need to create roles based permissions systems in my Rails app. I would be totally happy with CanCan, but the main problem - it has to be dynamic, so that Admin has to be able to assign permissions and to create new roles. The permissions can be simple controller/action restrictions, and can be data related, for example some users can edit only their own profiles, and some of them can edit the profiles of all the users in the particular group. And it would be really nice to allow Admin to create new permissions.
What I'm thinking about is to store in db a controller/action, and some data related restrictions (I'm really confused here about the way to define them). So could you please give me some advice, what would be the best way to organize permissions?
Any thoughts are much appreciated
If you like CanCan, then I think is best to use it. Here is a short tutorial about storing abilities in database so non-programmers can update them:
https://github.com/ryanb/cancan/wiki/Abilities-in-Database
If you really, really want to implement such system yourself. Depending on your needs, I will suggest for you to implement it as simple as possible.
In the case you need only users to have access to modules(certain controllers). You can do:
Store all users permissions in just like serialized fields -> http://apidock.com/rails/ActiveRecord/Base/serialize/class
class User
serialize :permissions, Array
def access_to?(module)
permissions.include? module.to_s
end
end
some check when setting this field would be nice.
Just make a check on top of every controller if the current user have access to this controller(section)
class ApplicationController
private
def self.require_access_to(module)
before_filter do |c|
unless c.send(:current_user).try :access_to?(module)
c.send :render_no_presmissions_page
end
end
end
end
class AdminNewsController
require_access_to :news
end
Of course this is just a start position, from where you can easily evolve.
[EDIT The link given by #RadoslavStankov is a better answer than this.]
You can do it with CanCan easily. Just make a model for permissions (which has_and_belongs_to_many :users), and in your Ability#initialize load the permissions appropriate to the user from the model and say that the user can do them. Then make appropriate user interface for administrating that model.
Something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
user.permissions.each do |permission|
can permission.symbol, permission.scope
end
user.prohibitions.each do |permission|
cannot permission.symbol, permission.scope
end
end
end
where scope returned something like :all for nil scope, or Object.const_get(#scope_name) otherwise... Play with it. Anyway, it's doable.
More complex CanCan things are likely to be more complex (duh) - for example conditional permissions - but that would be a bitch to administer as well, so I think this should be enough for most applications.
Related
If I set up a basic User model in Rails, and give it an is_admin:boolean, default: false attribute, what's the best way to prevent non-admin users from changing this?
It seems like the sort of logic that should really go into the model, but what's the best way to construct it? ActiveRecord callback functions?
I know I could put this into the controller's #update method, but that doesn't seem to match MVC best practices. (And seems less portable.)
What's the best approach here?
Denying access with a redirect is a job perfect for a controller, so doing it in private before_filter method would be sufficient and justified in my opinion.
Why do you say that the logic should go in the model?
It depends on your implementation. If the logic is dependent on the current_user (i.e. can a current admin set another user to be an admin as well), then the logic should go in the controller. Since you tagged the question as Rails 4, the logic will go in the permitted params in your controller:
def user_params
if current_user.is_admin
params.require(:user).permit(...your attributes here with is_admin...)
else
params.require(:user).permit(...your attributes here without is_admin...)
end
end
I would set admin privileges only in the console like that:
a = User.find_by(email: "user#example.com")
a.toggle!(:admin)
You would prevent somebody cracking into your site to gain admin privileges.
If I've got a simple rails user model that has an array of roles, is it sufficient enough to control access to actions by simply checking the model's role attribute for that role and blocking/proceeding accordingly?
Is there an advanced system that I ought to leverage due to unforeseen complexity?
Important: I'm not looking to authorize users/roles to models (I am already aware of CanCan). I'm looking to do security at the controller level so that I can break out the functionality in finer detail.
Even more important: Seriously, I'm not necessarily asking about CanCan, please read the question carefully and pay attention! :)
Question 1: YES, Question 2: NO.
I just keep this simple
If you check the models attribute in the controller, the controller will restrict all users that do not have this attribute set.
ex:
def create
#user.find(params[:user_id])
if #user.admin?
#post.new(params[:post])
#post.create!
end
end
make a method in the user model
def admin?
role == "Admin"
end
You should make better code than this. To much logic in the controller, but this will keep all, except admins out.
I want to be able to allow my admin users to be able to control almost every action that their standard users on the account can do. When creating group permissions for them to manage, is it better to have a giant table with over one hundred rows of booleans, or is it better to store all the permissions in a hash stored in a text field on the database? Maybe only store the things they can't do? or the things they can do? (whichever list is most often smaller?)
Is there a standard approach to doing this in webapps?
Some examples of what I'd store:
can_delete_object?
can_edit_object?
can_create_object?
can_delete_minions_object?
can_delete_managers_object?
I really like the can_? syntax that can-can uses.
Can-can would be great if it were anything other than defining functions for doing things. Which I still might end up doing in addition to storing all these booleans.. because there are account level permissions that will override the group level permissions.
[edit] - clarified the code a bit
I think you should really check out CanCan again if there is any kind of grouping of permissions (ie group users into managers, minions... with similar permissions and just save that field to the db instead of a whole hash of abilities)
heres an example of what the Ability class might look like in your situation:
class Ability
include CanCan::Ability
def initialize(user)
# All registered users can edit their own objects
can :manage, [Object], :user_id => user.id
# Different roles
if user.moderator?
can :delete, [Object], :user_id => user.minion.id
elsif user.admin?
can :delete, [Object], :user_id => user.manager.id
end
end
end
Here is a nice list of Ruby authentication gems.
I've used CanCan and acl9 in depth, and have reviewed several of the others. I agree with the recommendation to use CanCan, but I also had decent success with the acl9 gem on a complex app requiring user, role, and group assigned rights management.
It depends on what the permissions are for exactly, perhaps something like CanCan is what you're after?
https://github.com/ryanb/cancan
Would you be able to describe a few of the permissions otherwise?
I have a significant Roles aspect to my site and my ability.rb file has grown to a behemoth. It now conducts 25 queries to check 'role' records every time load_and_authorize_resource is called. Most of these queries are irrelevant because they govern lots of different permissions other than those relevant to the controller in use.
Let's say if on the companies controller, I only want to check permissions for Company and Project, and I want to ignore Task, Employee, and Assignment.
How can I send parameters to ability.rb so it only perform the relevant queries each time?
Hmm, one suggestion could be, that you could define your own ability models for Company and Project.
Something that quickly jumps into mind is the same concept that is being used here.
Perhaps you can override the current_ability method in the Company and Project controllers respetively. Something like:
def current_ability
#current_ability ||= CompanyAbility.new(current_user)
end
And in your CompanyAbility model, that is where you would define your custom permission settings solely for Company.
Hope that helps.
In my online store, users are allowed to change certain properties of their orders (e.g., their billing address), but not others (e.g., the origination ip address). Administrators, on the other hand, are allowed to modify all order properties.
Given, this, how can I use :attr_accessible to properly secure my Order model? Or will I have to use it to mark accessible all attributes that administrators can modify and refrain from using Order.update_attributes(params[:order]) in those controller actions that ordinary users can access?
Generally speaking, attr_accessible is not the tool you're looking for and Rails doesn't come with anything built in that does what you want.
If you want fine-grained control over who can update specific attributes in a model, you could do something like:
class Order < ActiveRecord::Base
def update_attributes_as_user(values, user)
values.each do |attribute, value|
# Update the attribute if the user is allowed to
#order.send("#{attribute}=", value) if user.can_modify?(attribute)
end
save
end
end
Then you can change your Order.update_attributes(params[:order]) to Order.update_attributes_as_user(params[:order], current_user) and assuming you implement the User#can_modify? method to return true in the correct cases, it should work.
I had the same problem and now I'm using this gem http://github.com/dmitry/attr_accessible_block
It's easy and used in some production website.
Yes, you'll have to modify the actions, so permissions are checked inside the actions. Calling Order#update_attributes will not work for the general user.
I can't rember a role-based authorization plugin that would allow something you are looking for. This is because these plugins mixin to the controllers and not the models. They would also need to mixin into ActiveRecord::Base to check for attr_accesible etc.