-
Notifications
You must be signed in to change notification settings - Fork 115
Role Subsystem
Role control subsystem has been lifted from Rails authorization plugin, but undergone some modifications.
It's based on two tables in the database. First, role table, which stores pairs [role_name, object] where object is a polymorphic model instance or a class. Second, join table, which joins users and roles.
To use this subsystem, you should define a Role model.
class Role < ActiveRecord::Base
acts_as_authorization_role
end
The structure of roles
table is as follows:
create_table "roles", :force => true do |t|
t.string :name
t.string :authorizable_type
t.integer :authorizable_id
t.timestamps null: false
end
add_index :roles, [:authorizable_type, :authorizable_id]
Note that you will almost never use the Role
class directly.
class User < ActiveRecord::Base
acts_as_authorization_subject :association_name => :roles
end
You won't need any specific columns in the users
table, but
there should be a join table:
create_table "roles_users", :id => false, :force => true do |t|
t.references :user
t.references :role
end
add_index :roles_users, :user_id
add_index :roles_users, :role_id
Place acts_as_authorization_object
call inside any model you want to act
as such.
class Foo < ActiveRecord::Base
acts_as_authorization_object
end
class Bar < ActiveRecord::Base
acts_as_authorization_object
end
A call of acts_as_authorization_subject
defines following methods on the model:
subject.has_role?(role, object = nil) # Returns `true` of `false` (has or has not)
subject.has_role!(role, object = nil) # Assigns a `role` for the `object` to the `subject`
subject.has_no_role!(role, object = nil) # Unassigns a role from the `subject`
subject.has_roles_for?(object) # Does the `subject` has any roles for `object`? (`true` of `false`)
subject.has_role_for?(object) # Same as `has_roles_for?`
subject.roles_for(object) # Returns an array of `Role` instances, corresponding to `subject`'s roles on object
subject.has_no_roles_for!(object) # Unassign any `subject`'s roles for a given `object`
subject.has_no_roles! # Unassign all roles from `subject`
A call of acts_as_authorization_object
defines following methods on the model:
object.accepts_role?(role_name, subject) # alias for `subject.has_role?(role_name, object)`
object.accepts_role!(role_name, subject) # alias for `subject.has_role!(role_name, object)`
object.accepts_no_role!(role_name, subject) # alias for `subject.has_no_role!(role_name, object)`
object.accepts_roles_by?(subject) # alias for `subject.has_roles_for?(object)`
object.accepts_role_by?(subject) # alias for `accepts_roles_by?`
object.accepts_roles_by(subject) # alias for `subject.roles_for(object)`
TODO - add the accepted_roles
stuff in here.
TODO - also add the object.users
and object.users :manager
interface.
You may want to deviate from default User
and Role
class names. That can easily be done with
arguments to acts_as_...
.
Say, you have Account
and AccountRole
:
class Account < ActiveRecord::Base
acts_as_authorization_subject :role_class_name => 'AccountRole'
end
class AccountRole < ActiveRecord::Base
acts_as_authorization_role :subject_class_name => 'Account'
end
class FooBar < ActiveRecord::Base
acts_as_authorization_object :role_class_name => 'AccountRole', :subject_class_name => 'Account'
end
Or... you can put the following in your config/initializers/acl9.rb
to affect all classes:
Acl9.configure do |c|
c.default_role_class_name = 'AccountRole'
c.default_subject_class_name = 'Account'
end
...and then in your models you can drop the explicit settings:
class Account < ActiveRecord::Base
acts_as_authorization_subject
end
class AccountRole < ActiveRecord::Base
acts_as_authorization_role
end
class FooBar < ActiveRecord::Base
acts_as_authorization_object
end
Note that you'll need to change your database structure appropriately:
create_table "account_roles", :force => true do |t|
t.string :name, :limit => 40
t.string :authorizable_type, :limit => 40
t.integer :authorizable_id
t.timestamps null: false
end
create_table "account_roles_accounts", :id => false, :force => true do |t|
t.references :account
t.references :account_role
end
user = User.create!
user.has_role? 'admin' # => false
user.has_role! :admin
user.has_role? :admin # => true
user
now has global role admin. Note that you can specify role name either
as a string or as a symbol.
foo = Foo.create!
user.has_role? 'admin', foo # => false
user.has_role! :manager, foo
user.has_role? :manager, foo # => true
foo.accepts_role? :manager, user # => true
user.has_roles_for? foo # => true
You can see here that global and object roles are distinguished from each other. User
with global role admin isn't automatically admin of foo
.
However,
user.has_role? :manager # => true
That is, if you have an object role, it means that you have a global role with the same name too!
In other words, you are manager if you manage at least one foo
(or a bar
...).
bar = Bar.create!
user.has_role! :manager, bar
user.has_no_role! :manager, foo
user.has_role? :manager, foo # => false
user.has_role? :manager # => true
Our user
is no more manager of foo
, but has become a manager of bar
.
user.has_no_roles!
user.has_role? :manager # => false
user.has_role? :admin # => false
user.role_objects # => []
At this time user
has no roles in the system.
The described role system with its 2 tables (not counting the users
table!)
might be an overkill for many cases. If all you want is global roles without
any scope, you'd better off implementing it by hand.
The access control subsystem of Acl9 uses only subject.has_role?
method, so
there's no need to implement anything else except for own convenience.
For example, if each your user can have only one global role, just add role
column to your User
class:
class User < ActiveRecord::Base
def has_role?(role_name, obj=nil)
self.role == role_name
end
def has_role!(role_name, obj=nil)
self.role = role_name
save!
end
end
If you need to assign multiple roles to your users, you can use serialize
with role array or a special solution like
preference_fu.
- Home
- Role Subsystem
- Access Control Subsystem
- Legacy Docs (some faults/errors may exist)