@@ -186,23 +186,153 @@ def permit!(privilege, options = {})
186
186
attr_validator = AttributeValidator . new ( self , user , options [ :object ] , privilege , options [ :context ] )
187
187
rules = matching_auth_rules ( roles , privileges , options [ :context ] )
188
188
189
- # Test each rule in turn to see whether any one of them is satisfied.
189
+ # _____ _ ____ ____
190
+ # / ___| (_) | _ \| _ \
191
+ # \ `--. _ __ _ ___ ___| | | | |_) |
192
+ # `--. \ '_ \| |/ __/ _ \ | | | _ <
193
+ # /\__/ / |_) | | (_| __/ |_| | |_) |
194
+ # \____/| .__/|_|\___\___|____/|____/
195
+ # | |
196
+ # |_|
197
+ use_spicedb_auth = false
198
+
190
199
rules . each do |rule |
191
- return true if rule . validate? ( attr_validator , options [ :skip_attribute_test ] )
200
+ unless rule . role . to_s . start_with? ( "leases__" , "lease_renewals__" )
201
+ # Existing behavior for non-lease-related rules
202
+ return true if rule . validate? ( attr_validator , options [ :skip_attribute_test ] )
203
+ next
204
+ end
205
+
206
+ use_spicedb_auth = true
207
+
208
+ auth_service_class = Rails . application . config . try ( :spicedb_authorization_service )
209
+ @auth_service = auth_service_class . new
210
+
211
+ vhost_id = Core ::Company . guid if defined? ( Core ::Company )
212
+ raise StandardError , "Vhost ID not found" if vhost_id . nil?
213
+
214
+ puts "\n ==== Processing new rule ===="
215
+ puts "Rule: #{ rule . inspect } "
216
+ puts "Role: #{ rule . role } "
217
+
218
+ permission_to_check = rule . role . to_s . gsub ( "__" , "_" ) + "_permission"
219
+ puts "Permission to check: #{ permission_to_check } "
220
+
221
+ if rule . attributes . empty?
222
+ puts "Rule has no attributes, checking spicedb directly"
223
+
224
+ authorized = @auth_service . check_permission (
225
+ resource : { type : "vhost" , id : vhost_id } ,
226
+ permission : permission_to_check
227
+ )
228
+ puts "Authorized? #{ authorized } "
229
+
230
+ return true if authorized
231
+ else
232
+ puts "Rule has #{ rule . attributes . count } attributes, examining them:"
233
+
234
+ rule . attributes . each_with_index do |attribute , index |
235
+ puts "\n -- Attribute ##{ index + 1 } : #{ attribute . inspect } "
236
+
237
+ if attribute . instance_variable_defined? ( '@conditions_hash' )
238
+ conditions = attribute . instance_variable_get ( '@conditions_hash' )
239
+ puts " Conditions hash: #{ conditions . inspect } "
240
+ else
241
+ puts " !! No conditions_hash instance variable found, skipping"
242
+ next
243
+ end
244
+
245
+ next unless conditions . is_a? ( Hash )
246
+
247
+ puts " Checking conditions against current values:"
248
+ condition_matched = false
249
+
250
+ if conditions . key? ( :granular_permissions )
251
+ rule_requires = conditions [ :granular_permissions ] [ 1 ]
252
+ actual_value = options [ :object ] &.granular_permissions if options [ :object ] . respond_to? ( :granular_permissions )
253
+
254
+ puts " Granular permissions - Rule requires: #{ rule_requires } , Actual: #{ actual_value } "
255
+ if rule_requires == actual_value
256
+ condition_matched = true
257
+ puts " ✓ Granular permissions condition matched!"
258
+ else
259
+ puts " ✗ Granular permissions condition did not match"
260
+ end
261
+ else
262
+ puts " (No granular_permissions condition to check)"
263
+ end
264
+
265
+ if conditions . key? ( :is_renewal )
266
+ rule_requires = conditions [ :is_renewal ] [ 1 ]
267
+ # If options[:object] has is_renewal, we'll use that value instead of obtaining it from SpiceDB.
268
+ # This allows for Blue Moon leases and others to be checked without having to make a SpiceDB call for the value of is_renewal.
269
+ if options [ :object ] . respond_to? ( :is_renewal )
270
+ actual_value = options [ :object ] . is_renewal
271
+ else
272
+ # Check if the lease document is a renewal. Ideally we implement a method in the auth service that is better
273
+ # suited for this kind of check, but for the POC we'll just use lookup_subjects since it does what we need.
274
+ puts " Checking SpiceDB for `renewal` relation on lease document uuid: #{ options [ :object ] &.lease_document_uuid . to_s } "
275
+ response = @auth_service . lookup_subjects (
276
+ resource_type : "lease_document" ,
277
+ resource_id : options [ :object ] &.lease_document_uuid . to_s ,
278
+ permission : "renewal" ,
279
+ subject_type : "lease_document" ,
280
+ )
281
+ actual_value = response [ :subject_ids ] . any?
282
+ end
283
+
284
+ puts " Is renewal - Rule requires: #{ rule_requires } , Actual: #{ actual_value } "
285
+ if rule_requires == actual_value
286
+ condition_matched = true
287
+ puts " ✓ Is_renewal condition matched!"
288
+ else
289
+ puts " ✗ Is_renewal condition did not match"
290
+ end
291
+ else
292
+ puts " (No is_renewal condition to check)"
293
+ 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
+
300
+ authorized = @auth_service . check_permission (
301
+ resource : { type : "vhost" , id : vhost_id } ,
302
+ permission : permission_to_check
303
+ )
304
+ puts " Authorized? #{ authorized } "
305
+
306
+ return true if authorized
307
+ else
308
+ puts " No matching conditions, skipping spicedb check for this attribute"
309
+ end
310
+ end
311
+ end
192
312
end
193
313
314
+ source_prefix = use_spicedb_auth ? "SpiceDB authorization failed. " : ""
315
+
194
316
if options [ :bang ]
195
317
if rules . empty?
196
318
raise NotAuthorized , "No matching rules found for #{ privilege } for User with id #{ user . try ( :id ) } " +
197
319
"(roles #{ roles . inspect } , privileges #{ privileges . inspect } , " +
198
320
"context #{ options [ :context ] . inspect } )."
199
321
else
200
- raise AttributeAuthorizationError , "#{ privilege } not allowed for User with id #{ user . try ( :id ) } on #{ ( options [ :object ] || options [ :context ] ) . inspect } ."
322
+ raise AttributeAuthorizationError , "#{ source_prefix } #{ privilege } not allowed for User with id #{ user . try ( :id ) } " +
323
+ "on #{ ( options [ :object ] || options [ :context ] ) . inspect } ."
201
324
end
202
325
else
203
326
false
204
327
end
205
- end
328
+ end
329
+ # _____ _ _ _____
330
+ # | ___| | \ | | | _ |
331
+ # | |__ | \| | | | | |
332
+ # | __| | . ` | | | | |
333
+ # | |___ | |\ | \ \_/ /
334
+ # \____/ \_| \_/ \___/
335
+
206
336
207
337
# Calls permit! but doesn't raise authorization errors. If no exception is
208
338
# raised, permit? returns true and yields to the optional block.
0 commit comments