Permalink
Browse files

Eliminate accidental privielege elevations, fixes #99

  • Loading branch information...
smathy committed Mar 14, 2018
1 parent 3310f4d commit b47255e2bb530e32ead0453c785490ed208172ba
View
@@ -14,3 +14,4 @@ matrix:
fast_finish: true
allow_failures:
- rvm: ruby-head
+ - gemfile: gemfiles/rails_5.2.gemfile
View
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- acl9 (3.0.0)
+ acl9 (3.1.0)
rails (~> 5.0)
GEM
View
@@ -35,6 +35,42 @@ def merge! h
def self.configure
yield config
end
+
+ class ArgumentError < ArgumentError; end
+ class RuntimeError < RuntimeError; end
+ class NilObjectError < RuntimeError; end
+
+ ##
+ # This exception is raised whenever ACL block finds that the current user
+ # is not authorized for the controller action he wants to execute.
+ # @example How to catch this exception in ApplicationController
+ # class ApplicationController < ActionController::Base
+ # rescue_from 'Acl9::AccessDenied', :with => :access_denied
+ #
+ # # ...other stuff...
+ # private
+ #
+ # def access_denied
+ # if current_user
+ # # It's presumed you have a template with words of pity and regret
+ # # for unhappy user who is not authorized to do what he wanted
+ # render :template => 'home/access_denied'
+ # else
+ # # In this case user has not even logged in. Might be OK after login.
+ # flash[:notice] = 'Access denied. Try to log in first.'
+ # redirect_to login_path
+ # end
+ # end
+ # end
+ #
+ class AccessDenied < RuntimeError; end
+
+ ##
+ # This exception is raised when acl9 has generated invalid code for the
+ # filtering method or block. Should never happen, and it's a bug when it
+ # happens.
+ class FilterSyntaxError < ArgumentError; end
+
end
ActiveRecord::Base.send(:include, Acl9::ModelExtensions)
@@ -12,6 +12,7 @@ def initialize(*args)
@allows = []
@denys = []
@original_args = args
+ @action_clause = nil
end
def acl_block!(&acl_block)
@@ -108,15 +109,15 @@ def _parse_and_add_rule(*args)
_set_action_clause( _retrieve_only(options), options.delete(:except))
- object = _role_object(options)
+ object_s = _role_object_s(options)
role_checks = args.map do |who|
case who
when anonymous then "#{_subject_ref}.nil?"
when logged_in then "!#{_subject_ref}.nil?"
when all then "true"
else
- "!#{_subject_ref}.nil? && #{_subject_ref}.has_role?('#{who}', #{object})"
+ "!#{_subject_ref}.nil? && #{_subject_ref}.has_role?('#{who}'#{object_s})"
end
end
@@ -179,13 +180,13 @@ def _action_check_expression(action_list)
end
end
- def _role_object(options)
+ def _role_object_s(options)
object = _by_preposition options
case object
- when Class then object.to_s
- when Symbol then _object_ref object
- when nil then "nil"
+ when Class then ", #{object}"
+ when Symbol then ", #{_object_ref object}"
+ when nil then ""
else
raise ArgumentError, "object specified by preposition can only be a Class or a Symbol"
end
@@ -1,37 +1,6 @@
require_relative "dsl_base"
module Acl9
- ##
- # This exception is raised whenever ACL block finds that the current user
- # is not authorized for the controller action he wants to execute.
- # @example How to catch this exception in ApplicationController
- # class ApplicationController < ActionController::Base
- # rescue_from 'Acl9::AccessDenied', :with => :access_denied
- #
- # # ...other stuff...
- # private
- #
- # def access_denied
- # if current_user
- # # It's presumed you have a template with words of pity and regret
- # # for unhappy user who is not authorized to do what he wanted
- # render :template => 'home/access_denied'
- # else
- # # In this case user has not even logged in. Might be OK after login.
- # flash[:notice] = 'Access denied. Try to log in first.'
- # redirect_to login_path
- # end
- # end
- # end
- #
- class AccessDenied < StandardError; end
-
- ##
- # This exception is raised when acl9 has generated invalid code for the
- # filtering method or block. Should never happen, and it's a bug when it
- # happens.
- class FilterSyntaxError < StandardError; end
-
module Dsl
module Generators
class BaseGenerator < Acl9::Dsl::Base
@@ -171,7 +140,7 @@ def #{@method_name}(*args)
raise ArgumentError, "call #{@method_name} with 0, 1 or 2 arguments"
end
- action_name = args.empty? ? self.action_name : args.first.to_s
+ self.action_name = args.first.to_s if args.present?
return #{allowance_expression}
end
@@ -5,6 +5,12 @@ module ModelExtensions
module ForSubject
include Prepositions
+ DEFAULT = Class.new do
+ def default?
+ true
+ end
+ end.new.freeze
+
##
# Role check.
#
@@ -38,16 +44,17 @@ module ForSubject
# @param [Object] object Object to query a role on
#
# @see Acl9::ModelExtensions::Object#accepts_role?
- def has_role?(role_name, object = nil)
+ def has_role?(role_name, object = default)
+ check! object
role_name = normalize role_name
object = _by_preposition object
- !! if object.nil? && !::Acl9.config[:protect_global_roles]
- self._role_objects.find_by_name(role_name.to_s) ||
- self._role_objects.member?(get_role(role_name, nil))
+ !! if object == default && !::Acl9.config[:protect_global_roles]
+ _role_objects.find_by_name(role_name.to_s) ||
+ _role_objects.member?(get_role(role_name, object))
else
role = get_role(role_name, object)
- role && self._role_objects.exists?(role.id)
+ role && _role_objects.exists?(role.id)
end
end
@@ -57,23 +64,24 @@ def has_role?(role_name, object = nil)
# @param [Symbol,String] role_name Role name
# @param [Object] object Object to add a role for
# @see Acl9::ModelExtensions::Object#accepts_role!
- def has_role!(role_name, object = nil)
+ def has_role!(role_name, object = default)
+ check! object
role_name = normalize role_name
object = _by_preposition object
role = get_role(role_name, object)
if role.nil?
role_attrs = case object
- when Class then { :authorizable_type => object.to_s }
- when nil then {}
- else { :authorizable => object }
- end.merge( { :name => role_name.to_s })
+ when Class then { :authorizable_type => object.to_s }
+ when default then {}
+ else { :authorizable => object }
+ end.merge({ :name => role_name.to_s })
- role = self._auth_role_class.create(role_attrs)
+ role = _auth_role_class.create(role_attrs)
end
- self._role_objects << role if role && !self._role_objects.exists?(role.id)
+ _role_objects << role if role && !_role_objects.exists?(role.id)
end
##
@@ -82,7 +90,8 @@ def has_role!(role_name, object = nil)
# @param [Symbol,String] role_name Role name
# @param [Object] object Object to remove a role on
# @see Acl9::ModelExtensions::Object#accepts_no_role!
- def has_no_role!(role_name, object = nil)
+ def has_no_role!(role_name, object = default)
+ check! object
role_name = normalize role_name
object = _by_preposition object
delete_role(get_role(role_name, object))
@@ -95,7 +104,8 @@ def has_no_role!(role_name, object = nil)
# @return [Boolean] Returns true if +self+ has any roles on +object+.
# @see Acl9::ModelExtensions::Object#accepts_roles_by?
def has_roles_for?(object)
- !!self._role_objects.detect(&role_selecting_lambda(object))
+ check! object
+ !!_role_objects.detect(&role_selecting_lambda(object))
end
alias :has_role_for? :has_roles_for?
@@ -112,14 +122,16 @@ def has_roles_for?(object)
#
# user.roles_for(product).map(&:name).sort #=> role names in alphabetical order
def roles_for(object)
- self._role_objects.select(&role_selecting_lambda(object))
+ check! object
+ _role_objects.select(&role_selecting_lambda(object))
end
##
# Unassign any roles on +object+ from +self+.
#
- # @param [Object,nil] object Object to unassign roles for. +nil+ means unassign global roles.
- def has_no_roles_for!(object = nil)
+ # @param [Object,default] object Object to unassign roles for. Empty args means unassign global roles.
+ def has_no_roles_for!(object = default)
+ check! object
roles_for(object).each { |role| delete_role(role) }
end
@@ -128,11 +140,11 @@ def has_no_roles_for!(object = nil)
def has_no_roles!
# for some reason simple
#
- # self.roles.each { |role| delete_role(role) }
+ # roles.each { |role| delete_role(role) }
#
# doesn't work. seems like a bug in ActiveRecord
- self._role_objects.map(&:id).each do |role_id|
- delete_role self._auth_role_class.find(role_id)
+ _role_objects.map(&:id).each do |role_id|
+ delete_role _auth_role_class.find(role_id)
end
end
@@ -142,7 +154,7 @@ def role_selecting_lambda(object)
case object
when Class
lambda { |role| role.authorizable_type == object.to_s }
- when nil
+ when default
lambda { |role| role.authorizable.nil? }
else
lambda do |role|
@@ -152,13 +164,14 @@ def role_selecting_lambda(object)
end
end
- def get_role(role_name, object)
+ def get_role(role_name, object = default)
+ check! object
role_name = normalize role_name
cond = case object
when Class
[ 'name = ? and authorizable_type = ? and authorizable_id IS NULL', role_name, object.to_s ]
- when nil
+ when default
[ 'name = ? and authorizable_type IS NULL and authorizable_id IS NULL', role_name ]
else
[
@@ -167,17 +180,17 @@ def get_role(role_name, object)
]
end
- if self._auth_role_class.respond_to?(:where)
- self._auth_role_class.where(cond).first
+ if _auth_role_class.respond_to?(:where)
+ _auth_role_class.where(cond).first
else
- self._auth_role_class.find(:first, :conditions => cond)
+ _auth_role_class.find(:first, :conditions => cond)
end
end
def delete_role(role)
if role
- if ret = self._role_objects.delete(role)
- if role.send(self._auth_subject_class_name.demodulize.tableize).empty?
+ if ret = _role_objects.delete(role)
+ if role.send(_auth_subject_class_name.demodulize.tableize).empty?
ret &&= role.destroy unless role.respond_to?(:system?) && role.system?
end
end
@@ -189,7 +202,11 @@ def normalize role_name
Acl9.config[:normalize_role_names] ? role_name.to_s.underscore.singularize : role_name.to_s
end
- protected
+ private
+
+ def check! object
+ raise NilObjectError if object.nil?
+ end
def _by_preposition object
object.is_a?(Hash) ? super : object
@@ -204,7 +221,11 @@ def _auth_role_assoc
end
def _role_objects
- send(self._auth_role_assoc)
+ send(_auth_role_assoc)
+ end
+
+ def default
+ DEFAULT
end
end
end
View
@@ -1,3 +1,3 @@
module Acl9
- VERSION = "3.0.0"
+ VERSION = "3.1.0"
end
@@ -121,7 +121,7 @@ class ActionTest < Base
assert set_all_actions
permit_some owner, @all_actions, :foo => foo
- permit_some hacker, %w(show index destroy)
+ permit_some hacker, %w(show index destroy), foo: foo
permit_some another_owner, %w(show index destroy), :foo => foo
end
Oops, something went wrong.

0 comments on commit b47255e

Please sign in to comment.