module PapersPlease class Policy attr_accessor :roles attr_reader :user def initialize(user) @user = user @roles = {} @cache = {} configure end def configure raise NotImplementedError, 'The #configure method of the access policy was not implemented' end # Add a role to the Policy def add_role(name, predicate = nil, &block) name = name.to_sym raise DuplicateRole if roles.key?(name) role = Role.new(name, predicate: predicate, definition: block) roles[name] = role role end alias role add_role # Add permissions to the Role def add_permissions(keys) return unless block_given? Array(keys).each do |key| raise MissingRole unless roles.key?(key) yield roles[key] end end alias permit add_permissions # Look up a stored permission block and call with # the current user and subject def can?(action, subject = nil) applicable_roles.each do |_, role| permission = role.find_permission(action, subject) next if permission.nil? # Proxy permission check if granted by other if permission.granted_by_other? # Get proxied subject subject = subject.is_a?(Class) ? permission.granting_class : permission.granted_by.call(user, subject) # Get proxied permission permission = role.find_permission(action, subject) end # Check permission return permission.granted?(user, subject, action) unless permission.nil? end false end def cannot?(*args) !can?(*args) end def authorize!(action, subject) raise AccessDenied, "Access denied for #{action} on #{subject}" if cannot?(action, subject) subject end # Look up a stored scope block and call with the # current user and class def scope_for(action, klass) applicable_roles.each do |_, role| permission = role.find_permission(action, klass) return permission.fetch(user, klass, action) unless permission.nil? end nil end alias query scope_for # Fetch roles that apply to the current user def applicable_roles @applicable_roles ||= roles.select do |_, role| role.applies_to?(user) end end end end