Class: Checken::Permission
- Inherits:
-
Object
- Object
- Checken::Permission
- Includes:
- Concerns::HasParents
- Defined in:
- lib/checken/permission.rb
Instance Attribute Summary collapse
-
#contexts ⇒ Array<Symbol>
readonly
The name of the contexts that apply to this permission.
-
#dependencies ⇒ Array<String>
readonly
A list of permission paths that this permission depends on.
-
#description ⇒ String
Return a description.
-
#group ⇒ Object
readonly
Returns the value of attribute group.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
-
#required_object_types ⇒ Array<String>
readonly
An array of object type names (as Strings) that the object passed to this permission must be one of.
Instance Method Summary collapse
-
#add_context(context) ⇒ Symbol, false
Add a new context to this permission.
-
#add_dependency(path) ⇒ String, false
Add a new dependency to this permission.
-
#add_required_object_type(type) ⇒ String, false
Add a new dependency to this permission.
-
#add_rule(key, rule = nil, &block) ⇒ Checken::Rule
Add a new rule to this permission.
-
#check!(user_proxy, object = nil) ⇒ Object
Check this permission and raises an error if not permitted.
-
#dependencies_as_permissions ⇒ Array<Checken::Permission>
Return an array of all dependencies as permissions.
- #dsl(&block) ⇒ Object
- #first_unsatisfied_included_rule(user_proxy, object) ⇒ Object
-
#first_unsatisfied_rule(user_proxy, object) ⇒ Checken::Rule, false
Check all the rules for this permission and ensure they are compliant.
-
#include_rule(key_or_existing_rule, options = {}, &block) ⇒ Checken::Rule
Add a new rule to this permission.
-
#included_rules ⇒ Hash
Return a hash of all configured included rules.
-
#initialize(group, key) ⇒ Permission
constructor
Create a new permission group.
-
#remove_all_contexts ⇒ Integer
Remove all context from this permission.
-
#rules ⇒ Hash
Return a hash of all configured rules.
- #update_schema ⇒ Object
Methods included from Concerns::HasParents
#parents, #path, #path_with_namespace, #root
Constructor Details
#initialize(group, key) ⇒ Permission
Create a new permission group
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/checken/permission.rb', line 40 def initialize(group, key) if group.nil? raise Error, "Group must be provided when creating a permission" end @group = group @schema = group.schema @key = key @required_object_types = [] @dependencies = [] @contexts = [] end |
Instance Attribute Details
#contexts ⇒ Array<Symbol> (readonly)
The name of the contexts that apply to this permission
34 35 36 |
# File 'lib/checken/permission.rb', line 34 def contexts @contexts end |
#dependencies ⇒ Array<String> (readonly)
A list of permission paths that this permission depends on
23 24 25 |
# File 'lib/checken/permission.rb', line 23 def dependencies @dependencies end |
#description ⇒ String
Return a description
18 19 20 |
# File 'lib/checken/permission.rb', line 18 def description @description end |
#group ⇒ Object (readonly)
Returns the value of attribute group.
12 13 14 |
# File 'lib/checken/permission.rb', line 12 def group @group end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
13 14 15 |
# File 'lib/checken/permission.rb', line 13 def key @key end |
#required_object_types ⇒ Array<String> (readonly)
An array of object type names (as Strings) that the object passed to this permission must be one of. If empty, any object is permitted.
29 30 31 |
# File 'lib/checken/permission.rb', line 29 def required_object_types @required_object_types end |
Instance Method Details
#add_context(context) ⇒ Symbol, false
Add a new context to this permission
198 199 200 201 202 203 204 205 206 |
# File 'lib/checken/permission.rb', line 198 def add_context(context) context = context.to_sym if self.contexts.include?(context) false else self.contexts << context context end end |
#add_dependency(path) ⇒ String, false
Add a new dependency to this permission
221 222 223 224 225 226 227 228 229 |
# File 'lib/checken/permission.rb', line 221 def add_dependency(path) path = path.to_s if dependencies.include?(path) false else dependencies << path path end end |
#add_required_object_type(type) ⇒ String, false
Add a new dependency to this permission
235 236 237 238 239 240 241 242 243 |
# File 'lib/checken/permission.rb', line 235 def add_required_object_type(type) type = type.to_s if required_object_types.include?(type) false else required_object_types << type type end end |
#add_rule(key, rule = nil, &block) ⇒ Checken::Rule
Add a new rule to this permission
152 153 154 155 156 157 158 159 160 |
# File 'lib/checken/permission.rb', line 152 def add_rule(key, rule = nil, &block) key = key.to_sym if rules[key].nil? rule ||= Rule.new(key, &block) rules[key] = rule else raise Error, "Rule with key '#{key}' already exists on this permission" end end |
#check!(user_proxy, object = nil) ⇒ Object
Check this permission and raises an error if not permitted.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/checken/permission.rb', line 67 def check!(user_proxy, object = nil) # If we havent' been given a user proxy here, we need to make one. This # shouldn't happen very often in production because everything would be # encapsulated by the User#can? method. unless user_proxy.is_a?(Checken::UserProxy) user_proxy = @group.schema.config.user_proxy_class.new(user_proxy) end # If we're asking about this permission and we aren't in the correct # context, it should be denied always. unless @contexts.empty? unless @contexts.any? { |c| user_proxy.contexts.include?(c) } @group.schema.logger.info "`#{self.path}` not granted to #{user_proxy.description} because not in context." error = PermissionDeniedError.new('NotInContext', "Permission '#{self.path}' cannot be granted in the #{user_proxy.contexts.join(',')} context(s). Only allowed for #{@contexts.join(', ')}.", self) error.user = user_proxy.user error.object = object raise error end end # Check the user has this permission if @group.schema.config.namespace && @group.schema.config.namespace_optional? = (user_proxy. & [self.path, self.path_with_namespace]).any? elsif @group.schema.config.namespace = user_proxy..include?(self.path_with_namespace) else = user_proxy..include?(self.path) end unless @group.schema.logger.info "`#{self.path}` not granted to #{user_proxy.description}" error = PermissionDeniedError.new('PermissionNotGranted', "User has not been granted the '#{self.path}' permission", self) error.user = user_proxy.user error.object = object raise error end # Check other dependent rules once we've established this # user has the base rule. The actual rules won't be checked # until we've checked other rules. .each do || @group.schema.logger.info "`#{self.path}` has a dependency of `#{dependency_permission.path}`..." .check!(user_proxy, object) end # Check any included rules too if unsatisifed_rule = self.first_unsatisfied_included_rule(user_proxy, object) @group.schema.logger.info "`#{self.path} not granted to #{user_proxy.description} because rule `#{unsatisifed_rule.rule.key}` on `#{self.path}` was not satisified." error = PermissionDeniedError.new('IncludedRuleNotSatisifed', "Rule #{unsatisifed_rule.rule.key} (on #{self.path}) was not satisified.", self) error.rule = unsatisifed_rule error.user = user_proxy.user error.object = object raise error end # Check rules if self.required_object_types.empty? || self.required_object_types.include?(object.class.name) if unsatisifed_rule = self.first_unsatisfied_rule(user_proxy, object) @group.schema.logger.info "`#{self.path} not granted to #{user_proxy.description} because rule `#{unsatisifed_rule.rule.key}` on `#{self.path}` was not satisified." error = PermissionDeniedError.new('RuleNotSatisifed', "Rule #{unsatisifed_rule.rule.key} (on #{self.path}) was not satisified.", self) error.rule = unsatisifed_rule error.user = user_proxy.user error.object = object raise error else @group.schema.logger.info "`#{self.path}` granted to #{user_proxy.description}" [self, *] end else # If one of the permission doesn't have the right object type, raise an error raise InvalidObjectError, "The #{object.class.name} object provided to permission check for #{self.path} was not valid. Valid object types are: #{self.required_object_types.join(', ')}" end end |
#dependencies_as_permissions ⇒ Array<Checken::Permission>
Return an array of all dependencies as permissions
301 302 303 304 305 |
# File 'lib/checken/permission.rb', line 301 def ||= dependencies.map do |path| @group.schema.root_group.(path) end.flatten end |
#dsl(&block) ⇒ Object
292 293 294 295 296 |
# File 'lib/checken/permission.rb', line 292 def dsl(&block) dsl = DSL::PermissionDSL.new(self) dsl.instance_eval(&block) if block_given? dsl end |
#first_unsatisfied_included_rule(user_proxy, object) ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/checken/permission.rb', line 260 def first_unsatisfied_included_rule(user_proxy, object) self.included_rules.values.each do |included_rule| if included_rule.condition && !included_rule.condition.call(user_proxy.user, object) # If the inclusion has a condition, check that and skip this # included rule if it's not valid. next end if included_rule.block translated_object = included_rule.block.call(object) else translated_object = object end rule = @group.all_defined_rules[included_rule.key] if rule.nil? raise Error, "No defined rule with key #{included_rule.key} is available for #{self.path}" end unless rule.required_object_types.empty? || rule.required_object_types.include?(translated_object.class.name) raise InvalidObjectError, "The #{translated_object.class.name} object provided to included rule (#{rule.key}) for #{self.path} was not valid. Valid object types are: #{rule.required_object_types.join(', ')}" end rule_execution = RuleExecution.new(rule, user_proxy.user, translated_object) unless rule_execution.satisfied? return rule_execution end end nil end |
#first_unsatisfied_rule(user_proxy, object) ⇒ Checken::Rule, false
Check all the rules for this permission and ensure they are compliant.
250 251 252 253 254 255 256 257 258 |
# File 'lib/checken/permission.rb', line 250 def first_unsatisfied_rule(user_proxy, object) self.rules.values.each do |rule| rule_execution = RuleExecution.new(rule, user_proxy.user, object) unless rule_execution.satisfied? return rule_execution end end nil end |
#include_rule(key_or_existing_rule, options = {}, &block) ⇒ Checken::Rule
Add a new rule to this permission
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/checken/permission.rb', line 173 def include_rule(key_or_existing_rule, = {}, &block) if key_or_existing_rule.is_a?(IncludedRule) key = key_or_existing_rule.key included_rule = key_or_existing_rule else key = key_or_existing_rule.to_sym included_rule = nil end if included_rules[key].nil? included_rule ||= begin new_rule = IncludedRule.new(key, &block) new_rule.condition = [:if] new_rule end included_rules[key] = included_rule else raise Error, "Rule with key '#{key}' already been included on this permission" end end |
#included_rules ⇒ Hash
Return a hash of all configured included rules
165 166 167 |
# File 'lib/checken/permission.rb', line 165 def included_rules @included_rules ||= {} end |
#remove_all_contexts ⇒ Integer
Remove all context from this permission
211 212 213 214 215 |
# File 'lib/checken/permission.rb', line 211 def remove_all_contexts previous_size = @contexts.size @contexts = [] previous_size end |
#rules ⇒ Hash
Return a hash of all configured rules
144 145 146 |
# File 'lib/checken/permission.rb', line 144 def rules @rules ||= {} end |
#update_schema ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/checken/permission.rb', line 307 def update_schema return if path.nil? group.schema.update_schema( { path_with_namespace => { type: :permission, description: description, group: group.path } } ) end |