Skip to content

Instantly share code, notes, and snippets.

@dux
Created October 27, 2016 10:49
Show Gist options
  • Save dux/7b95fd273699b7cb29cb767d6483e7b8 to your computer and use it in GitHub Desktop.
Save dux/7b95fd273699b7cb29cb767d6483e7b8 to your computer and use it in GitHub Desktop.
few simple policy objects
class DefaultPolicy < Policy
def before(action)
# return true for admins
admin?
end
def admin?
@user.try(:is_admin?) || false
end
def my?
return false unless @user
@user.id == @model.created_by
end
end
class ModelPolicy < DefaultPolicy
class << self
# ModelPolicy.can?(:write, @user) check UserPolicy.can?(:write, @user) if UserPolicy exists
# if not, it will fallback to ModelPolicy
def policy_class(model)
return self if self != ModelPolicy
"#{model.class}Policy".constantize rescue ModelPolicy
end
end
###
def read?
true
end
def create?
!!@user
end
def update?
my?
end
def delete?
my?
end
end
# current user actions
# alias for current_user.can?(...) but works with guests
# ---
# Policy.can?(:login?) -> ApplicationPolicy.can?(:login?)
# Policy.can?(:create, @product) -> ModelPolicy.can? ...
# Policy.can?(:update, @product) -> ProductPolicy.can? ...
# check if some other user can do action
# @user.can?(:update, @product)
# base caller
# Policy.check?(@user, action, model, &block)
# UserPolicy.can?(:update, @user) -> can current user update @user
# UserPolicy.can?(:update, @rental) -> can current user update @rental
# UserPolicy.check?(@user, :update, @rental) -> can @user update @rental
# call without block
# begin
# ApplicationPolicy.can?(:login)
# rescue
# raise UnauthorizedError, 'Err test_can: no access'
# end
# block will capture error message and be triggered only if error is present
# ApplicationPolicy.can?(:login) { |msg| http_error 401, "Err: #{msg}".red; return 'no access' }
class Policy
class << self
# assumes @user User.current
def can?(action, argument=nil, &block)
check?(User.current, action, argument, &block)
end
# default action
def check?(user, action, argument=nil, &block)
policy_class(argument).new(user, action, argument).call(&block)
end
# you can provide some other class for a given argument
def policy_class(argument=nil)
self
end
end
###
def initialize(user, action, argument=nil)
action = action.to_s.sub('?','') + '?'
action = action.to_sym
raise NoMethodError, %[Policy check "#{action}" not found in #{self.class}] unless respond_to?(action)
@user = user
@action = action
@model = @argument = argument
end
def call(&block)
return true if before(@action)
return true if send(@action)
raise UnauthorizedError, "access disabled in policy"
rescue UnauthorizedError
error = $!.message
error += "- #{self.class}.#{@action}" if Lux.dev?
raise UnauthorizedError, error unless block
block.call(error)
false
end
###
def before(action)
false
end
def error(message)
raise UnauthorizedError, message
end
end
class UserPolicy < ModelPolicy
def create?
false
end
def read?
my?
end
def update?
my?
end
def delete?
false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment