I'm actually not sure if this is a Pundit or general permissions architectural problem, but I setup a simple Pundit policy to restrict the actions a member within a company can perform. Users are joined as a Member to a company in a has_many, through:
relationship. The Member model has a role
attribute of owner
or user
.
Given a User that is a member of a Store, how can I restrict the access in a controller for the User's association to the Store? Below is a Admin::MembersController
where a store owner can invite other members. How can I restrict this to the given User in pundit through their member association to the store? The policy below doesn't work, returning an array of records. If I were to check against only the first record it works but I feel that is because of my limited understanding.
All of the tutorials and documentation I see online for CCC and Pundit involve application-wide permissions. But I need more granular control.
For example, my application has hundreds of companies. Each company has a user who is an "owner" and they login each day to look at their earnings information. That owner/user wants to invite Joe Smith to the application so they can also look at the data and make changes. But they don't want Joe Smith to be able to see certain types of data. So we restrict Joe Smith's access to certain data for that company.
class Admin::MembersController < Admin::BaseController
def index
@company_members = current_company.members
authorize([:admin, @company_members])
end
end
Policy
class Admin::MemberPolicy < ApplicationPolicy
def index?
return [ record.user_id, record.store_id ].include? user.id
## this works return [ record.first.user_id, record.first.store_id ].include? user.id
end
end
User.rb
class User < ApplicationRecord
# Automatically remove the associated `members` join records
has_many :members, dependent: :destroy
has_many :stores, through: :members
end
Member.rb
class Member < ApplicationRecord
belongs_to :store
belongs_to :user
enum role: [ :owner, :user ]
end
Store.rb
class Store < ApplicationRecord
has_many :members
has_many :users, through: :members
end
I got some insight from the contributors on Pundit; the most reasonable way to go about it this is to use a domain object which represents the context that a user is in - there is information about this in the Readme (https://github.com/varvet/pundit#additional-context). The UserContext object will provide references to a user and organization.
class ApplicationController
include Pundit
def pundit_user
if session[:organization_id]
UserContext.new(current_user, Organization.find(session[:organization_id]))
else
UserContext.new(current_user)
end
end
end
class UserContext
attr_reader :user, :organization
def initialize(user, organization = nil)
@user = user
@organization = organization
end
end