-
Reading through the documentation I can't figure out the recommended way to authorize a nested controller's index action when using rails. In particular my issue is passing information about the resources the controller is nested under to action_policy. Example DescriptionTo illustrate my problem, let's say I'm writing a holiday request system for HR. There are normal users who can create requests, can view their own requests ( There are also users with the "approver" role, who can view every user's requests and profile page. For this example the UX flow for an approver approving a request would be:
Example CodeAs requests are owned by a user, they are a nested resource. So my Rails.application.routes.draw do
resources :users do
resources :requests
end
end With the following policies defined under class UserPolicy < ApplicationPolicy
default_rule :manage?
def manage?
return true if user.approver?
user.id == record.id
end
end
class RequestPolicy < ApplicationPolicy
default_rule :manage?
def manage?
return true if user.approver?
user.id == record.user.id
end
end ProblemAt the moment I could authorize class RequestsController < ApplicationController
def index
@user = User.find(params[:user_id])
# If you can view a user's profile, you can view their request page
authorize! @user, to: :show?
@requests = @user.requests.all
end
end But it feels like this is a policy decision that should be kept in class RequestPolicy < ApplicationPolicy
default_rule :manage?
def index
allowed_to?(:show?, owner)
end
def manage?
return true if user.approver?
user.id == record.id
end
end Which could then be called like: class RequestsController < ApplicationController
def index
@owner = User.find(params[:user_id])
authorize!
@requests = @owner.requests.all
end
end I looked into using action_policy contexts, but the documentation states this is about adding extra context information about the subject, and in my case we are adding extra context about the resource (or maybe I've got this wrong and it is subject information?) Is there a recommended way to solve this problem? Should I continue calling different policies from my controller code? Or is there a way to add extra data apart from the |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
To my opinion, your original implementation ( However, I see a different (maybe, even better) way of handling this situation with Action Policy. We may consider a user to be the authorization context, why not? If we agree on this, we can then use optional authorization context in the policy: class RequestPolicy < ApplicationPolicy
authorize :owner, optional: true
default_rule :manage?
def index
allowed_to?(:show?, owner)
end
end And then in your controller you write: def index
@owner = User.find(params[:user_id])
# pass owner explicitly
authorize! context: {owner: @owner}
@requests = @owner.requests.all
end |
Beta Was this translation helpful? Give feedback.
To my opinion, your original implementation (
authorize! @user, to: :show?
) is correct: whenever you deal with a nested resource and a collection action (like#index
), you should authorize access to the current scope (parent resource). That's how I've been dealing with this problem for a long time (even before Action Policy).However, I see a different (maybe, even better) way of handling this situation with Action Policy. We may consider a user to be the authorization context, why not? If we agree on this, we can then use optional authorization context in the policy: