Closed radeno closed 10 years ago
@radeno,
Let me see if I understand correctly.
I think you're saying that, for class-level checks, we need to specify the account. In other words, we can't just say "can the current user create documents?", but rather, "can the current user create documents for the current account?"
Using Authority, you'd pose this question in your views as:
current_user.can_create? Media::Document, account: current_account
In your authorizer, you might do something like:
class DocumentAuthorizer
def self.creatable_by?(user, options = {})
account = options.fetch(:account) { raise "You must specify an account" }
[:admin, :author].any? { |role| user.roles_for_account(account).include?(role) }
end
end
For instance-level checks, I'm guessing that you can look up the resource's account? For example:
current_user.can_edit? @document
and in the authorizer:
def editable_by?(user)
account = resource.account # we know which account this document belongs to
# ... etc
end
The only tricky part is the controller. authorize_actions_for
sets up a before_filter
for you, but any options you pass are considered options for the filter itself (like except: :create
or whatever).
To also pass in the current_account
, you'll need to roll your own call to before_filter
(or before_action
, since Rails 4), like this:
class Media::DocumentsController < ApplicationController
before_filter except: :create { authorize_action_for Media::Document, account: current_account }
# ...
end
Finally, since you won't be using authorize_actions_for
, if you need to specify any special controller action mappings, use authority_actions
. For example:
authority_actions :breed => 'create', :vaporize => 'delete`
Let me know if I've misunderstood something or if you have any more questions.
@nathanl
thank you for you response and aim me to do this!
Yes i mean current user have permisionios defined by role for specific account (account is like namespace).
I'm so much influenced by simplicity of CanCan that takes me some time to implement this solution with Authority and is really great that is possible.
Please give me some time to try it. I believe that this will be new core gem of all my new projects.
R.
Hi again.
It seems there should be a way to use this authorization instead CanCan. Great! :) It is little handy to define class and instance of this class separately. And if is system more complex (Accounts, Roles, Users, Licenses, Groups) it is more harder to maintenance. CanCan was great that unified it (but bad, is abandoned). I find in yours TODO that want to improve it. Good info ;)
One question,
is there any solution how to filter on database layer within Authority? (not necessary if SQL, NoSQL or flat file)
For example to reduce of non necessary data. I know that it is possible to filter data in ruby, but it is more faster to do this first step in database.
Or it is only possible in own controller actions?
CanCan has database adapters. Do you plan add some like this? I think there is many projects which want to migrate from CanCan to another one with easiness of original solution.
R.
is there any solution how to filter on database layer within Authority?
There's currently no built-in way to filter records with Authority. What you can do is define scopes on your model and use them in your authorizer. For instance:
class Article
# Note that this is pseudocode; I haven't tested it
scope :accessible_to, lambda { |user|
return self.all if user.admin?
{ :conditions => { :user_id => user.id }
}
end
In your controller:
class ArticlesController < ApplicationController
def index
@articles = Article.accessible_to(current_user)
end
end
and in your authorizer:
class ArticleAuthorizer < ApplicationAuthorizer
def editable_by?(user)
# See http://stackoverflow.com/a/1255929/4376
resource.class.accessible_to(user).exists?(resource)
end
end
CanCan has database adapters. Do you plan add some like this?
No. One of the goals of Authority is to be neutral about what database or ORM you use. It works with all of them, but the tradeoff is that it requires you to do the work of looking up records.
I'd consider adding support for scopes, but only in the ORM-neutral way that, for example, Searchlight uses them (https://github.com/nathanl/searchlight)
@nathanl
Thank you for answer. So i must write database filter layer by own. Maybe it is disadvantage cause vast majority of all permission restrictions are with model attributes defined by ORM and their associations to another models. e.g. One user (editor) can update all Articles and another (author) can update only own, third one (contributor) can update only own and not published yet (state draft). Or specific editor can update articles only from defined categories, not all (ORM association, database join). And let imagine that there are thousands of articles with rich count of attributes and. It is performance issue. So nearly same logic defined in authority permission model must be duplicated to controller and it very complicated to maintain. I see authorization as integrated process.
I know that Authority is database agnostic and you can't agree with me.
And of course, i found very helpful way how to automatically push information about current_role and current_account to user. It is not necessary to use more parameters. Put to User model this:
cattr_accessor :current_role
cattr_accessor :current_account
And in Application controller: current_role and current_account are controller methods returns active account and role of user for that account.
before_action :initialize_user_role
before_action :initialize_user_account
private
def initialize_user_role
Access::User.current_role ||= current_role
end
def initialize_user_account
Access::User.current_account ||= current_account
end
so in user instance are accessible both methods.
If this will work, I will write short wiki for users with same problem as i have.
I know that Authority is database agnostic and you can't agree with me.
Actually, see #65; I think maybe we can do something that will be helpful after all.
Regarding this:
def initialize_user_role
Access::User.current_role ||= current_role
end
Two things I would caution: 1) it looks like you're modifying the Access::User class itself. If so, that's not threadsafe; you want to do that on an instance, not the class. 2) If this setup is required for all authorization, it's fine for your controllers to set it up during the request cycle, but be aware that this means you can't do authorization during, say, a rake task without some additional work there.
Sorry for my time delay. I currently migrate quite large project from Rails 3 to Rails 4, and its little bit confused :)
Hi,
i'm migrate from CanCan to Authority and i stuck on one thing.
My App has one more layer than only user and role authorizing. It is Account authorizing.
So every user should belongs to more accounts with different roles.
Account is layer, that separate clients against each other. Like
For example: user 1 belongs to account 1 with role admin user 1 belongs to account 2 with role author
User must be switched only in one account. So if i'm switched in Account 2 i have permissions only as author for that account.
Can you please hint me, how to do this?
In my controller i have helpers current_account, current_role
With CanCan it was ease, because there should be initialize with more arguments:
Thank you for help.
R.