I have User and Role models in many-to-many relations via UserRoleAssoc in Ruby-on-Rails.
I need a page (web interface) from which a user can add/delete roles associated with a user, where ordinary users but administrators can edit the roles for themselves only.
My question is how to implement the scheme, particularly authorization.
Here are the models of User and Role (just the standard many-to-many):
class User < ApplicationRecord
has_many :user_role_assocs, dependent: :destroy
has_many :roles, through: :user_role_assocs
end
class Role < ApplicationRecord
has_many :user_role_assocs
has_many :users, through: :user_role_assocs
end
class UserRoleAssoc < ApplicationRecord
belongs_to :user
belongs_to :role
end
According to DHH's principle (cf. "How DHH Organizes His Rails Controllers" by Jerome Dalbert), such actions should be implemented as if a controller, say, ManageUserRolesController
, does one or more of the CRUD actions. In this case, ManageUserRolesController
either or both of create
and delete
multiple records on UserRoleAssoc
.
Since the web user interface should enable one to manage a list of roles (with a select box) in one go from a URL, I made the create
method of ManageUserRolesController
does both, receiving User-ID (user
) and an Array of Role-IDs (roles
) in params
(I'm open to suggestions, though!). routes.rb
is as follows:
resources :manage_user_role, only: [:create] # index may be provided, too.
Now, to restrict a user to add/delete roles to any other users, I would like to write in models/ability.rb
something like, along with a Controller:
# models/ability.rb`
can :create, ManageUserRoles, :PARAMS => {user: user} # "PARAMS" is invalid!! Any alternative ideas?
can :manage, ManageUserRoles if user.administrator?
# controllers/manage_user_roles_controller.rb
class ManageUserRolesController < ApplicationController
load_and_authorize_resource
end
It seems possible to achieve it in the way described in an answer to "Passing params to CanCan in RoR" and CanCan wiki, though I think the model corresponding to the controller has to be defined to point the non-standard table, in models/manage_user_role.rb
class ManageUserRole < ApplicationRecord
self.table_name = 'user_role_assocs'
end
But this seems quite awkward…
What is the 'Rails' way (Version 6+) to implement authorization of many-to-many models? To be specific, what is a good interface to add/delete multiple roles to a user with some constraint?
Note that the route doesn't have to be like the sample code above; the route can be set so that a user-ID is passed as a part of the path like /manage_user_role/:user_id
instead of via params
, as long as authorization works.