can_be_authorized.rb 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. module CanBeAuthorized
  3. extend ActiveSupport::Concern
  4. =begin
  5. true or false for permission
  6. user = User.find(123)
  7. user.permissions?('permission.key') # access to certain permission.key
  8. user.permissions?(['permission.key1', 'permission.key2']) # access to permission.key1 or permission.key2
  9. user.permissions?('permission') # access to all sub keys
  10. user.permissions?('permission.*') # access if one sub key access exists
  11. returns
  12. true|false
  13. =end
  14. def permissions?(auth_query)
  15. RequestCache.permissions?(self, auth_query)
  16. end
  17. class RequestCache < ActiveSupport::CurrentAttributes
  18. attribute :permission_cache
  19. def self.permissions?(authorizable, auth_query)
  20. self.permission_cache ||= {}
  21. begin
  22. authorizable_key = authorizable.to_global_id.to_s
  23. rescue
  24. return instance.permissions?(authorizable, auth_query)
  25. end
  26. auth_query_key = Array(auth_query).join('|')
  27. self.permission_cache[authorizable_key] ||= {}
  28. self.permission_cache[authorizable_key][auth_query_key] ||= instance.permissions?(authorizable, auth_query)
  29. end
  30. def permissions?(authorizable, auth_query)
  31. verbatim, wildcards = acceptable_permissions_for(auth_query)
  32. authorizable.permissions.where(name: verbatim).then do |base_query|
  33. wildcards.reduce(base_query) do |query, name|
  34. query.or(authorizable.permissions.where('permissions.name LIKE ?', name.sub('.*', '.%')))
  35. end
  36. end.exists?
  37. end
  38. private
  39. def acceptable_permissions_for(auth_query)
  40. Array(auth_query)
  41. .reject { |name| Permission.lookup(name: name)&.active == false } # See "chain-of-ancestry quirk" in spec file
  42. .flat_map { |name| Permission.with_parents(name) }.uniq
  43. .partition { |name| name.end_with?('.*') }.reverse
  44. end
  45. end
  46. end