role.rb 8.5 KB

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