@@ -214,6 +214,7 @@ def permit!(privilege, options = {})
214
214
puts "\n ==== Processing new rule ===="
215
215
puts "Rule: #{ rule . inspect } "
216
216
puts "Role: #{ rule . role } "
217
+ puts "Join Operator: #{ rule . join_operator } " if rule . respond_to? ( :join_operator )
217
218
218
219
permission_to_check = rule . role . to_s . gsub ( "__" , "_" ) + "_permission"
219
220
puts "Permission to check: #{ permission_to_check } "
@@ -230,6 +231,9 @@ def permit!(privilege, options = {})
230
231
return true if authorized
231
232
else
232
233
puts "Rule has #{ rule . attributes . count } attributes, examining them:"
234
+
235
+ all_attributes_matched = true
236
+ any_attribute_matched = false
233
237
234
238
rule . attributes . each_with_index do |attribute , index |
235
239
puts "\n -- Attribute ##{ index + 1 } : #{ attribute . inspect } "
@@ -245,24 +249,22 @@ def permit!(privilege, options = {})
245
249
next unless conditions . is_a? ( Hash )
246
250
247
251
puts " Checking conditions against current values:"
248
- condition_matched = false
252
+ current_attribute_matched = true
249
253
250
254
if conditions . key? ( :granular_permissions )
251
255
rule_requires = conditions [ :granular_permissions ] [ 1 ]
252
256
actual_value = options [ :object ] &.granular_permissions if options [ :object ] . respond_to? ( :granular_permissions )
253
257
254
258
puts " Granular permissions - Rule requires: #{ rule_requires } , Actual: #{ actual_value } "
255
259
if rule_requires == actual_value
256
- condition_matched = true
257
260
puts " ✓ Granular permissions condition matched!"
258
261
else
259
262
puts " ✗ Granular permissions condition did not match"
263
+ current_attribute_matched = false
260
264
end
261
- else
262
- puts " (No granular_permissions condition to check)"
263
265
end
264
266
265
- if conditions . key? ( :is_renewal )
267
+ if conditions . key? ( :is_renewal ) && current_attribute_matched
266
268
rule_requires = conditions [ :is_renewal ] [ 1 ]
267
269
# If options[:object] has is_renewal, we'll use that value instead of obtaining it from SpiceDB.
268
270
# This allows for Blue Moon leases and others to be checked without having to make a SpiceDB call for the value of is_renewal.
@@ -276,36 +278,73 @@ def permit!(privilege, options = {})
276
278
resource_type : "lease_document" ,
277
279
resource_id : options [ :object ] &.lease_document_uuid . to_s ,
278
280
permission : "renewal" ,
279
- subject_type : "lease_document" ,
281
+ subject_type : "lease_document"
280
282
)
281
- actual_value = response [ :subject_ids ] . any?
283
+ actual_value = response . any?
282
284
end
283
285
284
286
puts " Is renewal - Rule requires: #{ rule_requires } , Actual: #{ actual_value } "
285
287
if rule_requires == actual_value
286
- condition_matched = true
287
288
puts " ✓ Is_renewal condition matched!"
288
289
else
289
290
puts " ✗ Is_renewal condition did not match"
291
+ current_attribute_matched = false
292
+ end
293
+ end
294
+
295
+ if conditions . key? ( :id ) && current_attribute_matched
296
+ condition_type = conditions [ :id ] [ 0 ]
297
+ condition_proc = conditions [ :id ] [ 1 ]
298
+
299
+ if condition_type == :id_in_scope && condition_proc . is_a? ( Proc )
300
+ puts " Checking Occupancy ID in scope condition"
301
+ user_has_access_to_occupancy = attribute . validate? ( attr_validator )
302
+ puts " User has access to occupancy? #{ user_has_access_to_occupancy } "
303
+ if user_has_access_to_occupancy
304
+ puts " ✓ This attribute matched conditions"
305
+ else
306
+ puts " ✗ This attribute did not match conditions"
307
+ current_attribute_matched = false
308
+ end
290
309
end
310
+ end
311
+
312
+ if current_attribute_matched
313
+ any_attribute_matched = true
314
+ puts " ✓ All conditions matched for this attribute"
291
315
else
292
- puts " (No is_renewal condition to check)"
316
+ all_attributes_matched = false
317
+ puts " ✗ Not all conditions matched for this attribute"
293
318
end
294
-
295
- if condition_matched
296
- # For now, we will assume each rule's conditions are OR'd together. This is not the case
297
- # for all rules, but it's the most common case and a good starting point.
298
- puts " At least one matching condition found, checking spicedb"
299
-
319
+ end
320
+
321
+ puts " Finished checking attributes, checking if rule is authorized"
322
+
323
+ if rule . respond_to? ( :join_operator ) && rule . join_operator == :and
324
+ puts " Rule uses AND operator, checking if all attributes matched: #{ all_attributes_matched } "
325
+ if all_attributes_matched
326
+ puts " All attributes matched, checking SpiceDB permission"
327
+ authorized = @auth_service . check_permission (
328
+ resource : { type : "vhost" , id : vhost_id } ,
329
+ permission : permission_to_check
330
+ )
331
+ puts " Authorized? #{ authorized } "
332
+ return true if authorized
333
+ else
334
+ puts " Not all attributes matched, authorization denied for this rule"
335
+ end
336
+ else
337
+ puts " Rule uses OR operator (default), checking if any attribute matched: #{ any_attribute_matched } "
338
+ if any_attribute_matched
339
+ puts " At least one attribute matched, checking SpiceDB permission"
300
340
authorized = @auth_service . check_permission (
301
341
resource : { type : "vhost" , id : vhost_id } ,
302
342
permission : permission_to_check
303
343
)
304
344
puts " Authorized? #{ authorized } "
305
-
306
345
return true if authorized
307
346
else
308
- puts " No matching conditions, skipping spicedb check for this attribute "
347
+ puts " No attributes matched, authorization denied for this rule "
309
348
end
310
349
end
311
350
end
0 commit comments