In my Rails app, there are 3 models, defined by a has_many :through
association:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
The join Administration
model has a role
attribute, that we use to define the role — Owner, Editor or Viewer — of a given user
for a given calendar
.
Indeed, in the app, a user can be Owner of a calendar, and Viewer of another calendar for instance.
I implemented authentication with Devise.
I have also started implementing authorization with Pundit: authorization is currently working for calendars
, where users
can perform different actions depending on their roles
.
UPDATE: here is the current CalendarPolicy
:
class CalendarPolicy < ApplicationPolicy
attr_reader :user, :calendar
def initialize(user, calendar)
@user = user
@calendar = calendar
end
def index?
user.owner?(calendar) || user.editor?(calendar) || user.viewer?(calendar)
end
def create?
true
end
def show?
user.owner?(calendar) || user.editor?(calendar) || user.viewer?(calendar)
end
def update?
user.owner?(calendar) || user.editor?(calendar)
end
def edit?
user.owner?(calendar) || user.editor?(calendar)
end
def destroy?
user.owner?(calendar)
end
end
Now, I would like to implement a Pundit policy for the Administration model, as follows:
- If a user is Owner of a calendar, then he can perform
Index
,Show
,Create
,New
,Edit
,Update
andDestroy
actions on the Administrations of this calendar. - But, if a user is Editor or viewer of a calendar, then he can only do two things: 1. perform
Index
action to see all the users of a calendar and 2. performDestroy
action on his own Administration to "leave the calendar".
My problem is the following:
- An Administration instance only exist as the connection between a
user
and acalendar
, as explained above. - So, to perform actions on an Administration instance, I need three pieces of context:
administration_id
,user_id
andcalendar_id
. - However, Pundit only accepts two pieces of context in a policy, generally the
user
and the actualrecord
(which would be administration here).
On the GitHub page of Pundit, in the Additional context section, we can read the following:
Additional context
Pundit strongly encourages you to model your application in such a way that the only context you need for authorization is a user object and a domain model that you want to check authorization for. If you find yourself needing more context than that, consider whether you are authorizing the right domain model, maybe another domain model (or a wrapper around multiple domain models) can provide the context you need.
Pundit does not allow you to pass additional arguments to policies for precisely this reason.
However, in very rare cases, you might need to authorize based on more context than just the currently authenticated user. Suppose for example that authorization is dependent on IP address in addition to the authenticated user. In that case, one option is to create a special class which wraps up both user and IP and passes it to the policy.
Does a has_many :through
association constitute one of the "very rare cases" mentioned above or is there a simpler way to implement authorization for my Administration
join model?