role.rb 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Role < ApplicationModel
  3. include HasDefaultModelUserRelations
  4. include CanBeImported
  5. include HasActivityStreamLog
  6. include ChecksClientNotification
  7. include ChecksHtmlSanitized
  8. include HasGroups
  9. include HasCollectionUpdate
  10. include HasSearchIndexBackend
  11. include CanSelector
  12. include CanSearch
  13. include Role::Assets
  14. has_and_belongs_to_many :users, after_add: :cache_update, after_remove: :cache_update
  15. has_and_belongs_to_many :permissions,
  16. before_add: %i[validate_agent_limit_by_permission validate_permissions],
  17. after_add: %i[cache_update cache_add_kb_permission],
  18. before_remove: :last_admin_check_by_permission,
  19. after_remove: %i[cache_update cache_remove_kb_permission]
  20. validates :name, presence: true, uniqueness: { case_sensitive: false }
  21. store :preferences
  22. has_many :knowledge_base_permissions, class_name: 'KnowledgeBase::Permission', dependent: :destroy
  23. before_save :cleanup_groups_if_not_agent
  24. before_create :check_default_at_signup_permissions
  25. before_update :last_admin_check_by_attribute, :validate_agent_limit_by_attributes, :check_default_at_signup_permissions
  26. # workflow checks should run after before_create and before_update callbacks
  27. include ChecksCoreWorkflow
  28. core_workflow_screens 'create', 'edit'
  29. # ignore Users because this will lead to huge
  30. # results for e.g. the Customer role
  31. association_attributes_ignored :users
  32. activity_stream_permission 'admin.role'
  33. validates :note, length: { maximum: 250 }
  34. sanitized_html :note
  35. =begin
  36. grant permission to role
  37. role.permission_grant('permission.key')
  38. =end
  39. def permission_grant(key)
  40. permission = Permission.lookup(name: key)
  41. raise "Invalid permission #{key}" if !permission
  42. return true if permission_ids.include?(permission.id)
  43. self.permission_ids = permission_ids.push permission.id # rubocop:disable Style/RedundantSelfAssignment
  44. true
  45. end
  46. =begin
  47. revoke permission of role
  48. role.permission_revoke('permission.key')
  49. =end
  50. def permission_revoke(key)
  51. permission = Permission.lookup(name: key)
  52. raise "Invalid permission #{key}" if !permission
  53. return true if permission_ids.exclude?(permission.id)
  54. self.permission_ids = self.permission_ids -= [permission.id]
  55. true
  56. end
  57. =begin
  58. get signup roles
  59. Role.signup_roles
  60. returns
  61. [role1, role2, ...]
  62. =end
  63. def self.signup_roles
  64. Role.where(active: true, default_at_signup: true)
  65. end
  66. =begin
  67. get signup role ids
  68. Role.signup_role_ids
  69. returns
  70. [role1, role2, ...]
  71. =end
  72. def self.signup_role_ids
  73. signup_roles.map(&:id)
  74. end
  75. =begin
  76. get all roles with permission
  77. roles = Role.with_permissions('admin.session')
  78. get all roles with permission "admin.session" or "ticket.agent"
  79. roles = Role.with_permissions(['admin.session', 'ticket.agent'])
  80. returns
  81. [role1, role2, ...]
  82. =end
  83. def self.with_permissions(keys)
  84. permission_ids = Role.permission_ids_by_name(keys)
  85. Role.joins(:permissions_roles).joins(:permissions).where(
  86. 'permissions_roles.permission_id IN (?) AND roles.active = ? AND permissions.active = ?', permission_ids, true, true
  87. ).distinct
  88. end
  89. =begin
  90. check if roles is with permission
  91. role = Role.find(123)
  92. role.with_permission?('admin.session')
  93. get if role has permission of "admin.session" or "ticket.agent"
  94. role.with_permission?(['admin.session', 'ticket.agent'])
  95. returns
  96. true | false
  97. =end
  98. def with_permission?(keys)
  99. permission_ids = Role.permission_ids_by_name(keys)
  100. return true if Role.joins(:permissions_roles).joins(:permissions).where(
  101. 'roles.id = ? AND permissions_roles.permission_id IN (?) AND permissions.active = ?', id, permission_ids, true
  102. ).distinct.count.nonzero?
  103. false
  104. end
  105. def self.permission_ids_by_name(keys)
  106. Array(keys).each_with_object([]) do |key, result|
  107. ::Permission.with_parents(key).each do |local_key|
  108. permission = ::Permission.lookup(name: local_key)
  109. next if !permission
  110. result.push permission.id
  111. end
  112. end
  113. end
  114. private
  115. def validate_permissions(permission)
  116. Rails.logger.debug { "self permission: #{permission.id}" }
  117. raise "Permission #{permission.name} is disabled" if permission.preferences[:disabled]
  118. permission.preferences[:not]
  119. &.find { |name| name.in?(permissions.map(&:name)) }
  120. &.tap { |conflict| raise "Permission #{permission} conflicts with #{conflict}" }
  121. permissions.find { |p| p.preferences[:not]&.include?(permission.name) }
  122. &.tap { |conflict| raise "Permission #{permission} conflicts with #{conflict}" }
  123. end
  124. def last_admin_check_by_attribute
  125. return true if !will_save_change_to_attribute?('active')
  126. return true if active != false
  127. return true if !with_permission?(['admin', 'admin.user'])
  128. raise Exceptions::UnprocessableEntity, __('At least one user needs to have admin permissions.') if last_admin_check_admin_count < 1
  129. true
  130. end
  131. def last_admin_check_by_permission(permission)
  132. return true if Setting.get('import_mode')
  133. return true if permission.name != 'admin' && permission.name != 'admin.user'
  134. raise Exceptions::UnprocessableEntity, __('At least one user needs to have admin permissions.') if last_admin_check_admin_count < 1
  135. true
  136. end
  137. def last_admin_check_admin_count
  138. admin_role_ids = Role.joins(:permissions).where(permissions: { name: ['admin', 'admin.user'], active: true }, roles: { active: true }).where.not(id: id).pluck(:id)
  139. User.joins(:roles).where(roles: { id: admin_role_ids }, users: { active: true }).distinct.count
  140. end
  141. def validate_agent_limit_by_attributes
  142. return true if Setting.get('system_agent_limit').blank?
  143. return true if !will_save_change_to_attribute?('active')
  144. return true if active != true
  145. return true if !with_permission?('ticket.agent')
  146. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent', active: true }, roles: { active: true }).pluck(:id)
  147. currents = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct.pluck(:id)
  148. news = User.joins(:roles).where(roles: { id: id }, users: { active: true }).distinct.pluck(:id)
  149. count = currents.concat(news).uniq.count
  150. raise Exceptions::UnprocessableEntity, __('Agent limit exceeded, please check your account settings.') if count > Setting.get('system_agent_limit').to_i
  151. true
  152. end
  153. def validate_agent_limit_by_permission(permission)
  154. return true if Setting.get('system_agent_limit').blank?
  155. return true if active != true
  156. return true if permission.active != true
  157. return true if permission.name != 'ticket.agent'
  158. ticket_agent_role_ids = Role.joins(:permissions).where(permissions: { name: 'ticket.agent' }, roles: { active: true }).pluck(:id)
  159. ticket_agent_role_ids.push(id)
  160. count = User.joins(:roles).where(roles: { id: ticket_agent_role_ids }, users: { active: true }).distinct.count
  161. raise Exceptions::UnprocessableEntity, __('Agent limit exceeded, please check your account settings.') if count > Setting.get('system_agent_limit').to_i
  162. true
  163. end
  164. def check_default_at_signup_permissions
  165. return true if !default_at_signup
  166. forbidden_permissions = permissions.reject(&:allow_signup)
  167. return true if forbidden_permissions.blank?
  168. raise Exceptions::UnprocessableEntity, "Cannot set default at signup when role has #{forbidden_permissions.join(', ')} permissions."
  169. end
  170. def cache_add_kb_permission(permission)
  171. return if !permission.name.starts_with? 'knowledge_base.'
  172. return if !KnowledgeBase.granular_permissions?
  173. KnowledgeBase::Category.all.each(&:touch)
  174. end
  175. def cache_remove_kb_permission(permission)
  176. return if !permission.name.starts_with? 'knowledge_base.'
  177. return if !KnowledgeBase.granular_permissions?
  178. has_editor = permissions.where(name: 'knowledge_base.editor').any?
  179. has_reader = permissions.where(name: 'knowledge_base.reader').any?
  180. KnowledgeBase::Permission
  181. .where(role: self)
  182. .each do |elem|
  183. if !has_editor && !has_reader
  184. elem.destroy!
  185. elsif !has_editor && has_reader
  186. elem.update!(access: 'reader') if elem.access == 'editor'
  187. end
  188. end
  189. KnowledgeBase::Category.all.each(&:touch)
  190. end
  191. def cleanup_groups_if_not_agent
  192. # #with_permissions? SQL-based check does not work on to-be-saved permissions
  193. # using application-side check instead
  194. return if permissions.any? { |elem| elem.name == 'ticket.agent' }
  195. groups.clear
  196. end
  197. end