I am trying to "extend" Rolify functionality to have some global roles such as 'Admin', 'Member', 'Guest', etc... and to be able to set up different "scopes" for each user who have a specific role.
For example, in my app i have this admin
role, which is a "super role" meaning it grants access to basically everything. But i also want to be able to "scope" this role for another User, the scope will be, for example 'he will have access to all users, but only if they are from countries A, B, C and from cities X, Y, Z'. I know rolify supports different roles with different scopes, but what i want is to manage "global roles" with different scopes only for different users.
I thought about doing something like a 'Scope' model that belongs to a Role and to a User, in which i would have HABTM relationships with countries and cities, and then use that for authorization (I'm using CanCanCan). But i ran into many issues when working on this approach. It was something like:
class Scope
belongs_to :user
belongs_to :role
has_and_belongs_to_many :countries
has_and_belongs_to_many :cities
end
One of the issues i ran into was that i need to grant the role at the same time i create a scope, and if a user is 'revoked' of a role, the scope which belongs to the user and the role, needs to be destroyed. This last part i found particularly hard since 'Scope' is not related to 'users_roles' table.
Anyone has any idea on a better approach to this problem? I'm having a hard time figuring out the right way to have a role that has custom scopes for each user (basically i need something in the middle of the user and the role to define what is the user's scope with that role).
Appreciate any help I can get!
CodePudding user response:
If you want to create something of your own has_and_belongs_to_many
is not the answer (hint: it's almost never the right answer). Using HABTM is the akilles heel of Rolify as its assocations look like this:
class User
has_and_belongs_to_many :roles
end
class Role
has_and_belongs_to_many :users
belongs_to :resource,
polymorphic: true,
optional: true
end
This doesn't let you you query the users_roles table directly or add additional columns or logic. Fixing it has been an open issue since 2013. There are workarounds but Rolify may not be the right tool for the job here anyways.
If you want to roll your own you want to use has_many through:
to setup an actual join model so you can query the join table directly and add assocations, additional columns and logic to it.
class User
has_many :user_roles
has_many :roles, through: :user_roles
end
class UserRole
belongs_to :user
belongs_to :role
belongs_to :resource,
polymorphic: true,
optional: true
validates_uniqueness_of :user_id,
scope: [:role_id, :resource_id, :resource_type]
end
class Role
validates :name, presence: true,
uniqueness: true
has_many :user_roles
has_many :roles, through: :user_roles
end
This moves the resource scoping from being per role to being per user.
While you could add additional join tables between the user_roles
table and the "scoped" resources its not strictly necissary unless you want to avoid polymorphic asssocations.