permissions_update.rb 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class KnowledgeBase
  3. class PermissionsUpdate
  4. def initialize(object, user = nil)
  5. @object = object
  6. @user = user
  7. end
  8. def update!(**roles_to_permissions)
  9. ActiveRecord::Base.transaction do
  10. ensure_unoverrideable_permissions!(roles_to_permissions)
  11. update_object(roles_to_permissions)
  12. next if !@object.changed_for_autosave?
  13. @object.save!
  14. update_all_children
  15. ensure_editable!
  16. end
  17. end
  18. def update_using_params!(params)
  19. roles_to_permissions = params[:permissions]
  20. .to_hash
  21. .transform_keys { |key| Role.find key }
  22. update!(**roles_to_permissions)
  23. end
  24. private
  25. def update_object(roles_to_permissions)
  26. @object.permissions.reject { |elem| roles_to_permissions.key? elem.role }.each(&:mark_for_destruction)
  27. roles_to_permissions.each do |role, access|
  28. update_object_permission(role, access)
  29. end
  30. end
  31. def update_object_permission(role, access)
  32. permission = @object.permissions.detect { |elem| elem.role == role } || @object.permissions.build(role: role)
  33. permission.access = access
  34. mark_permission_for_cleanup_if_needed(permission, parent_object_permissions)
  35. end
  36. def parent_object_permissions
  37. @parent_object_permissions ||= begin
  38. if @object.is_a? KnowledgeBase::Category
  39. (@object.parent || @object.knowledge_base).permissions_effective || []
  40. else
  41. []
  42. end
  43. end
  44. end
  45. def all_children
  46. case @object
  47. when KnowledgeBase::Category
  48. @object.self_with_children - [@object]
  49. when KnowledgeBase
  50. @object.categories.root.map(&:self_with_children).flatten
  51. end
  52. end
  53. def update_single_child(child)
  54. inherited_permissions = (child.parent || child.knowledge_base).permissions_effective
  55. child.permissions.each do |child_permission|
  56. mark_permission_for_cleanup_if_needed(child_permission, inherited_permissions)
  57. end
  58. child.changed_for_autosave? ? child.save! : child.touch # rubocop:disable Rails/SkipsModelValidations
  59. end
  60. def update_all_children
  61. all_children.each do |child|
  62. update_single_child(child)
  63. end
  64. end
  65. def ensure_editable!
  66. return if !@user
  67. return if KnowledgeBase::EffectivePermission.new(@user, @object).access_effective == 'editor'
  68. raise Exceptions::UnprocessableEntity, __('Invalid permissions, do not lock yourself out.')
  69. end
  70. def mark_permission_for_cleanup_if_needed(permission, parents)
  71. matching = parents.find { |elem| elem.role == permission.role }
  72. return if !matching
  73. return if matching.access == 'reader' && permission.access != 'reader'
  74. permission.mark_for_destruction
  75. end
  76. def ensure_unoverrideable_permissions!(new_roles_permissions)
  77. new_roles_permissions.each do |role, new_permission|
  78. ensure_single_unoverrideable_permission!(role, new_permission)
  79. end
  80. end
  81. def ensure_single_unoverrideable_permission!(role, new_permission)
  82. parent_permission = parent_object_permissions.find { |elem| elem.role == role }
  83. return if parent_permission.nil?
  84. return if parent_permission.access == 'reader'
  85. return if parent_permission.access == new_permission
  86. message = case parent_permission.access
  87. when 'editor'
  88. __('Invalid permissions. This role has editor access to parent category. Limiting access is not effective.')
  89. when 'none'
  90. __('Invalid permissions. This role does not have access to this category because parent category is not visible for it.')
  91. else
  92. __('Invalid permissions.')
  93. end
  94. raise Exceptions::UnprocessableEntity, message
  95. end
  96. end
  97. end