I'm using devise for authentication and I'm looking for an authorization framework that lets me declare edit permissions for specific model attributes.
I have three different roles in my app: Teacher, Parent, and Student. The Student model belongs_to Family. When a Teacher creates a Student, they are able to set the Family association. When a Parent visits the edit page for a Student, however, they should not be able to change that association, only view it.
In the view, it's easy to alter the form depending on who is viewing it (disable or don't disable the family select input, for example) but a crafted form can get around that. What I need is something that will throw some kind of authorization exception when someone tries to change an attribute that they are not allowed to change.
I'm currently looking at declarative_authorization, but it seems it's not fine-grained enough to restrict changes to attributes, only the model as a whole.
I've ended up using the new MassAssignmentSecurity feature, although it looks like it might not work that great in conjunction with accepts_nested_attributes_for.
I realize my answer comes 2 years late. For what it's worth what you need is an authorization framework that is fine-grained enough.
XACML, the standard from OASIS provides just that. It can handle any number of attributes.
See my detailed answer here: Rails 4 authorization gem
Related
App Attributes:
Rails 3.2, Mongoid, Devise, Elasticsearch
Basic Structure:
User has a Post
Post can be seen by other users
Situation:
A User wants to remove the post from being view-able by other users for a period of time.
Note
Eventually I would like to extend this capability so that its possible for said user to be able to make it only viewable to certain colleagues. This way they can collaborate on the post together.
Problem:
I can't seem to think of a good way in rails to do this. One idea I had was to create a Boolean field in the post model that would allow me to achieve some of this but the ACL's would tricky and unstable at best. So I'm reaching out to the great intelligence this should be cake for some of you.
The solution you suggest is the one that occurs to me. Place a boolean flag private on the Post model, and modify the code you use to fetch Posts to exclude ones with the flag set unless they belong to the current user, (depending on whether private posts are viewable by their owner in that particular context).
I haven't actually worked with Mongoid, but I believe this is as simple as Post.any_of({private: false}, {user_id: current_user.id})
If and when you implement the collaboration functionality you discuss, all you need to do is change that code again - fetch posts that are not private, or that belong to any one of a given set of users.
I'm diving into RBAC while designing new and rather big/complex site.
I'm trying to figure out if to create a task or simply an operation with biz rule.
Now, I've read most if not all existing documentation. The current documentation says that "a task consists of operations". This wiki article says that the different terms are simply naming conventions and the only limitation that exists is structural one - roles must include tasks (or other roles); tasks should include operations (or other tasks) and operations is the atomic term that is not further composed by other entities.
I've also read the relevant sections in the "Agile web dev..." and "Yii cookbook" books - both do not shed further light on this issue (at least as seen through my glasses).
Lets go to my example where I'll present the question. Actually, lets use an example similar to that demonstrated in most of the documentation resources mentioned above: Lets say I have a blog post and I want/need to have its author be able to "update own post". Now, why should this be a task as commonly demonstrated in the documentation resources and not an operation with a biz rule?
I think that the question above reveals the inclear definition of a "task" (in the RBAC context of course).
Please help me distill a better definition for an RBAC task.
EDIT:
I was suggested the following definitions of the mentioned terms that help conceptualize them in a useful way. In short and in its simplest form: operations are the basic building blocks. They are the material developers work with and only them. Developers compose tasks of and on top of operations. Roles are composed of tasks, like a set of tasks. Roles and tasks are what the site administrators should play with - assign and revoke to users but not operations.
That's a nice way to look and grasp those entities (roles, tasks and operations).
Do you have another option to conceptualize differently? Any comments will be appreciated.
TIA!
Boaz.
I'd say the same as you did in your question edit. A task is simply a composition of operations a user can do that have something in common. So you have for example operations oList, oView, oCreate and oUpdate these are the operation developer assigns to controller actions for access control, where the first two are only read- and the second two have write access to data (that's what they have in common). So you now want to combine those to tasks tInspect and tManage which both hold 2 operations, the first one can list and view and the second one can create and update. Optionally you could make tInspect a sub-task of tManage so a user that has tManage can list, view, update and create but normally you just give his role both tasks.
Regarding the classification of role -> task -> operation, they are essentially the same thing, as you can see in the code they are of class CAuthItem. We name them differently mainly from user point of view.
Operations are only used by developers and they represent the finest level of permission.
Tasks are built on top of operations by developers. They represent the basic building units to be used by RBAC administrators.
Roles are built on top of tasks by administrators and may be assigned to users or user groups.
The above is a recommendation, not requirement. In general, administrators can only see tasks and roles, while developers only care about operations and tasks.
Check this out : http://www.yiiframework.com/forum/index.php/topic/2313-rbac-confusion/page_p_16035#entry16035
if there are two user
1)admin
2)user
so we set role updatePost for update page.
and admin is parent of updatePost so admin can update.
user have updateOwnPost permission.updateOwnPost is parent of updatePost with bizrule.so if bizrule satisfy he can update
I've got different roles on my RPG website. Each user can have many roles and based on this can access features. Let's say for instance a user is a teacher and director, he should access a page to manage his subjects and another to manage all the website.
Some people have more than 5 roles and it becomes really awkward to have 5 links to each office in the header. How can I have an action to include others based on roles ?
In fact, I want to show all the offices available for a user on a unique page including offices managed by single actions. How is this possible ?
Thank you in advance !
PS: Actions are in different controllers
If I got you right.
I think it sounds like a user's privilege problem. If it is, I recommend Ryan's gem "CanCan". It's super easy to use.
CanCan
And its Railscast: #192 Authorization with CanCan
Make a chain of command, with role-weights. The heaviest role merges a lot of somehow related small ones.
Try the Cancan gem which has a way of constraining database operations based on roles.
Example from docs:
#articles = Article.accessible_by(current_ability)
Currently I am developing an API and within that API I want the signed in users to be able to like/unlike or favorite/unfavorite two resources.
My "Like" model (it's a Ruby on Rails 3 application) is polymorphic and belongs to two different resources:
/api/v1/resource-a/:id/likes
and
/api/v1/resource-a/:resource_a_id/resource-b/:id/likes
The thing is: I am in doubt what way to choose to make my resources as RESTful as possible. I already tried the next two ways to implement like/unlike structure in my URL's:
Case A: (like/unlike being the member of the "resource")
PUT /api/v1/resource/:id/like maps to Api::V1::ResourceController#like
PUT /api/v1/resource/:id/unlike maps to Api::V1::ResourceController#unlike
and case B: ("likes" is a resource on it's own)
POST /api/v1/resource/:id/likes maps to Api::V1::LikesController#create
DELETE /api/v1/resource/:id/likes maps to Api::V1::LikesController#destroy
In both cases I already have a user session, so I don't have to mention the id of the corresponding "like"-record when deleting/"unliking".
I would like to know how you guys have implemented such cases!
Update April 15th, 2011: With "session" I mean HTTP Basic Authentication header being sent with each request and providing encrypted username:password combination.
I think the fact that you're maintaining application state on the server (user session that contains the user id) is one of the problems here. It's making this a lot more difficult than it needs to be and it's breaking a REST's statelessness constraint.
In Case A, you've given URIs to operations, which again is not RESTful. URIs identify resources and state transitions should be performed using a uniform interface that is common to all resources. I think Case B is a lot better in this respect.
So, with these two things in mind, I'd propose something like:
PUT /api/v1/resource/:id/likes/:userid
DELETE /api/v1/resource/:id/likes/:userid
We also have the added benefit that a user can only register one 'Like' (they can repeat that 'Like' as many times as they like, and since the PUT is idempotent it has the same result no matter how many times it's performed). DELETE is also idempotent, so if an 'Unlike' operation is repeated many times for some reason then the system remains in a consistent state. Of course you can implement POST in this way, but if we use PUT and DELETE we can see that the rules associated with these verbs seem to fit our use-case really well.
I can also imagine another useful request:
GET /api/v1/resource/:id/likes/:userid
That would return details of a 'Like', such as the date it was made or the ordinal (i.e. 'This was the 50th like!').
case B is better, and here have a good sample from GitHub API.
Star a repo
PUT /user/starred/:owner/:repo
Unstar a repo
DELETE /user/starred/:owner/:repo
You are in effect defining a "like" resource, a fact that a user resource likes some other resource in your system. So in REST, you'll need to pick a resource name scheme that uniquely identifies this fact. I'd suggest (using songs as the example):
/like/user/{user-id}/song/{song-id}
Then PUT establishes a liking, and DELETE removes it. GET of course finds out if someone likes a particular song. And you could define GET /like/user/{user-id} to see a list of the songs a particular user likes, and GET /like/song/{song-id} to see a list of the users who like a particular song.
If you assume the user name is established by the existing session, as #joelittlejohn points out, and is not part of the like resource name, then you're violating REST's statelessness constraint and you lose some very important advantages. For instance, a user can only get their own likes, not their friends' likes. Also, it breaks HTTP caching, because one user's likes are indistinguishable from another's.
Struggling with a decision on how best to handle Client-level authentication with the following model hierarchy:
Client -> Store -> Product (Staff, EquipmentItem, etc.)
...where Client hasMany Stores, Store hasMany Products(hasMany Staff, hasMany EquipmentItem, etc.)
I've set up a HABTM relationship between User and Client, which is straightforward and accessible through the Auth session or a static method on the User model if necessary (see afterFind description below).
Right now, I'm waffling between evaluating the results in each model's afterFind callback, checking for relationship to Client based on the model I'm querying against the Clients that the current User is a member of. i.e. if the current model is Client, check the id; if the current model is a Store, check Store.clientid, and finally if Product, get parent Store from Item.storeid and check Store.clientid accordingly.
However, to keep in line with proper MVC, I return true or false from the afterFind, and then have to check the return from the calling action -- this is ok, but I have no way I can think of to determine if the Model->find (or Model->read, etc.) is returning false because of invalid id in the find or because of Client permissions in the afterFind; it also means I'd have to modify every action as well.
The other method I've been playing with is to evaluate the request in app_controller.beforeFilter and by breaking down the request into controller/action/id, I can then query the appropriate model(s) and eval the fields against the Auth.User.clients array to determine whether User has access to the requested Client. This seems ok, but doesn't leave me any way (afaik) to handle /controller/index -- it seems logical that the index results would reflect Client membership.
Flaws in both include a lengthy list of conditional "rules" I need to break down to determine where the current model/action/id is in the context of the client. All in all, both feel a little brittle and convoluted to me.
Is there a 3rd option I'm not looking at?
This sounds like a job for Cake ACL. It is a bit of a learning curve, but once you figure it out, this method is very powerful and flexible.
Cake's ACLs (Access Control Lists) allow you to match users to controllers down to the CRUD (Create Read Update Delete) level. Why use it?
1) The code is already there for you to use. The AuthComponent already has it built in.
2) It is powerful and integrated to allow you to control permissions every action in your site.
3) You will be able to find help from other cake developers who have already used it.
4) Once you get it setup the first time, it will be much easier and faster to implement full site permissions on any other application.
Here are a few links:
http://bakery.cakephp.org/articles/view/how-to-use-acl-in-1-2-x
http://book.cakephp.org/view/171/Access-Control-Lists
http://blog.jails.fr/cakephp/index.php?post/2007/08/15/AuthComponent-and-ACL
Or you could just google for CakePHP ACL