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.
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.
I'm new to RoR and am utilizing CanCan for authorization. I'm wondering if I have to be explicit with every single ability that a user may have.
I'm probably not being clear enough, so I'll post some code to help illustrate my question.
if user.is_admin?
can :manage, all
end
if user.is_director?
can :update, Camp
end
In this case, would the director only be able to update a camp? Or would I have to denote what he/she specifically cannot do as well?
Thanks in advance.
If you added check_authorization to your ApplicationController, then yes, it will default to be locked down for all controller actions, unless specifically overwritten by skip_authorization_check.
See this GitHub discussion around this issue.
If I remember correctly the moment you implement CanCan you have to explicit, as long as you call the authorize_resource (or load_and_authorize_resource) in the matching controller.
So yes (, if you call one of authorize methods in the CampController).
(Also the director role should not overlap with the admin role ;) If it does it just get all permissions. And isn't it :all?)
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.
I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u
I want to implement authorization in my Rails application on a model level (not controller), in a similar way that validation on models is done. What is the best way to do this?
If it is implemented in the models itself, the main problem is that the models don't have access to the current user. I've seen solutions like: Thread.current[:user_id] = session[:user_id], but that doesn't seem like a good idea.
I've seen a different approach where variants of the methods like create, find and new are created, accepting an additional parameter for the current user.
Another approach would be to implement all the methods in the User/role class, so for example user.posts.create or user.readable_posts.find would be used instead of Post.create or Post.find.
Which of these approaches would be suggested? Are there any better ways to implement the authorization? Are there any plugins that makes this easier? I need an approach that scales well for multiple roles and models.
I would recommend you to look at declarative authorization. It works with both models and controllers.
The way it do what you are asking is having a before_filter in the applicationController that sets Authorization.current_user = current_user where Authorization is a module.
I think that approach is the best one, it keeps the models clean and you don't have to remember to include the user everywhere, but can filter it in the models callback functions instead.
Why would you do this? Isn't controller level identification enough?
if #user.is_able_to_do_this?
do_this
else
blah!
end
... and in your model
def is_able_to_do_this
if self.rights > Constant::Admin_level_whatever
return true
end
return false
end