Using :attr_accessible with role-based authorization - ruby-on-rails

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.

Related

Modelling multiple types of users with Devise and Rails 4

I have 3 types of "users"
Shop
Provider
Customer
The last 2 users will have similar attributes such as
First Name
Last Name
and so on
And the Shop user type will have the most contrast between types.
As far as behaviours they will all be quite different, although Provider and Shop will inherent many of customer behaviours.
It seems the behaviours can be dealt with CanCan as I've researched.
I'm now attempting to how I should authenticate these types.
I have looked at the STI model but I couldn't grasp where I would these extra attributes.
My mental model is as follower:
User is a table and model
The types are abstract models that inherit from this.
So I'm wondering, how do I add attributes such as Business address for just the Shop type?
Or is it that the User Table has a column called Type and that type is associated with these type tables? And within the type tables are the extra attributes?
Don't even bother bringing Devise into this; Devise is for authentication not authorization. Authentication is determining whether or not someone who visits your site is who you think they are, like logging in. Authorization is deciding whether or not a user is allowed to perform some sort of action, like creating a new post.
What you want to do is have some sort of system that assigns a normal user your three different types; CanCan will do something like that. One way to do this on your own is using a permissions number based system. Let's say normal users have permissions level at 100, shop has a level at 50, and provider at 25. Using this system you can determine what actions a user can perform without having to make separate models, which will make your schema unnecessarily complicated. Here's an example of how this would work with say the UserController:
def show
if current_user.permissions == 100
render "customer_show"
elsif current_user.permissions == 50
render "shop_show"
else
render "provider_show"
end
end
The easiest way to do this is to add a column to the user's table called permissions that defaults to say 100 when a new row is created. Here's what that migration would look like:
def change
add_column :users, :permissions, :integer, default: 100
end
As for authenticating, don't worry about it. Let devise do it's thing; every user, no matter what type, will login and sign up in the same way, maybe just having separate forms for each that has a hidden field to set the permissions level for that specific kind of user.
I know I'm late to the party but I'm putting this out for future SO searchers. You CAN authorize actions with Devise. If you have devise models for 'buyer' & 'seller' you can add 'buyer_signed_in?' for whatever action you only want buyers to be able to do. You can also do more specific role-based authorizations as well - check out Devise' page
All in all, Tsiege's solution sounds pretty interesting. Let us know if you had any success with it!

Rails acts_as_tenant - access current tenant

I have added the gem 'acts_as_tenant' to my Rails app.
I have set the following in the application controller:
set_current_tenant_by_subdomain(:tenant, :subdomain)
I have also added columns of information to the Tenants table. For instance the statuscode_id for a 'closed' status. In the Request model, I would like to scope using that column.
Something like this:
scope :closed, where(:statuscode_id => Tenant.find(current_tenant.id).request_closed)
But, current_tenant isn't working.
What can I use to get the id of the current tenant?
Thanks!
All you've done so far is say that the subdomain will be used to define the tenant. In order to restrict the tenant to only see their data you need to define this in the model.
You need to add acts_as_tenant(:tenant) to your Request model. You will also need a tenant_id column in the database for this table.
Once you've done this, the gem will take care of the rest and all your database access will automatically take care of restricting access by tenant. e.g. Request.all will use default scope behind the scenes to restrict to only the requests owned by that tenant.
Please see ErwinM/acts_as_tenant for more information on this.

Authorization for actions (not models!) with roles in rails

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.

Dynamic roles and permissions system in Rails app

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.

Rails saving IP address with every create/update request

I'd like to do the following:
define a before_filter in application.rb that extracts the user's IP address and stores it anywhere, preferably in the session.
define two before filters in all my models as before_create and before_update that add the current user's IP to the object to be stored.
Problem: I cannot access session[] neither env[] in a model. Can anyone help with a standard solution that I don't know yet?
Regards
Jason
Try this. In your user model add a class attribute accessor
cattr_accessor :current_ip
In your application controller add:
before_filter :set_current_ip
protected
def set_current_ip
User.current_ip = request.env['REMOTE_ADDR']
end
Then in your model you should be able to just call User.current_ip
We do something similar to get the current_user object passed through.
You're having trouble doing what you want because Rails is designed not to allow you to have access to session information in your models. It's the classic separation of concerns with MVC. Models are meant to work independently of your other layers, and you'll be thankful they do when you start doing things with Rake or other system tasks where you won't have a session.
The
cattr_accessor :current_ip
is a horrible approach. It's a hack and it should be apparent why. Yes, it may work, but it's the wrong approach to this problem.
Since you're tracking "who" did "what" by their IP, the logical place for this to happen is in the controller layer. There are several approaches you can take, including using CacheSweepers as auditors, as outlined in the Rails Recipes book. CacheSweepers can observe models but also have access to all controller information. Using the ditry attributes in a rails model, you can see exactly what changed.
#user = User.find_by_login "bphogan"
#user.login = "Brian"
#user.save
#user.changed
=> ["login"]
#user.changes
=> {"login"=>["bphogan", "brian"]}
#user.login_was
=> "bphogan"
Combine this with the session info you have and you have a pretty awesome auditor.
Does that help?
If you want to save the IP in the session, you can create a before filter in the applicationController. Like this, for each action, the filter is called and the ip is stored.
authlogic is a plugin to manage users login/sessions etc, it has a built in option to track the users IP
What you really need is a versioning plugin - I suggest having a look at one of the fine solutions at http://ruby-toolbox.com/categories/activerecord_versioning.html
Edit: archived version of that link (was 404 since sometime in 2012): https://web.archive.org/web/20111004161536/http://ruby-toolbox.com:80/categories/activerecord_versioning.html

Resources