You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
5.0 KiB

  1. # frozen_string_literal: true
  2. # == Schema Information
  3. #
  4. # Table name: user_roles
  5. #
  6. # id :bigint(8) not null, primary key
  7. # name :string default(""), not null
  8. # color :string default(""), not null
  9. # position :integer default(0), not null
  10. # permissions :bigint(8) default(0), not null
  11. # highlighted :boolean default(FALSE), not null
  12. # created_at :datetime not null
  13. # updated_at :datetime not null
  14. #
  15. class UserRole < ApplicationRecord
  16. FLAGS = {
  17. administrator: (1 << 0),
  18. view_devops: (1 << 1),
  19. view_audit_log: (1 << 2),
  20. view_dashboard: (1 << 3),
  21. manage_reports: (1 << 4),
  22. manage_federation: (1 << 5),
  23. manage_settings: (1 << 6),
  24. manage_blocks: (1 << 7),
  25. manage_taxonomies: (1 << 8),
  26. manage_appeals: (1 << 9),
  27. manage_users: (1 << 10),
  28. manage_invites: (1 << 11),
  29. manage_rules: (1 << 12),
  30. manage_announcements: (1 << 13),
  31. manage_custom_emojis: (1 << 14),
  32. manage_webhooks: (1 << 15),
  33. invite_users: (1 << 16),
  34. manage_roles: (1 << 17),
  35. manage_user_access: (1 << 18),
  36. delete_user_data: (1 << 19),
  37. }.freeze
  38. module Flags
  39. NONE = 0
  40. ALL = FLAGS.values.reduce(&:|)
  41. DEFAULT = FLAGS[:invite_users]
  42. CATEGORIES = {
  43. invites: %i(
  44. invite_users
  45. ).freeze,
  46. moderation: %w(
  47. view_dashboard
  48. view_audit_log
  49. manage_users
  50. manage_user_access
  51. delete_user_data
  52. manage_reports
  53. manage_appeals
  54. manage_federation
  55. manage_blocks
  56. manage_taxonomies
  57. manage_invites
  58. ).freeze,
  59. administration: %w(
  60. manage_settings
  61. manage_rules
  62. manage_roles
  63. manage_webhooks
  64. manage_custom_emojis
  65. manage_announcements
  66. ).freeze,
  67. devops: %w(
  68. view_devops
  69. ).freeze,
  70. special: %i(
  71. administrator
  72. ).freeze,
  73. }.freeze
  74. end
  75. attr_writer :current_account
  76. validates :name, presence: true, unless: :everyone?
  77. validates :color, format: { with: /\A#?(?:[A-F0-9]{3}){1,2}\z/i }, unless: -> { color.blank? }
  78. validate :validate_permissions_elevation
  79. validate :validate_position_elevation
  80. validate :validate_dangerous_permissions
  81. validate :validate_own_role_edition
  82. before_validation :set_position
  83. scope :assignable, -> { where.not(id: -99).order(position: :asc) }
  84. has_many :users, inverse_of: :role, foreign_key: 'role_id', dependent: :nullify
  85. def self.nobody
  86. @nobody ||= UserRole.new(permissions: Flags::NONE, position: -1)
  87. end
  88. def self.everyone
  89. UserRole.find(-99)
  90. rescue ActiveRecord::RecordNotFound
  91. UserRole.create!(id: -99, permissions: Flags::DEFAULT)
  92. end
  93. def self.that_can(*any_of_privileges)
  94. all.select { |role| role.can?(*any_of_privileges) }
  95. end
  96. def everyone?
  97. id == -99
  98. end
  99. def nobody?
  100. id.nil?
  101. end
  102. def permissions_as_keys
  103. FLAGS.keys.select { |privilege| permissions & FLAGS[privilege] == FLAGS[privilege] }.map(&:to_s)
  104. end
  105. def permissions_as_keys=(value)
  106. self.permissions = value.map(&:presence).compact.reduce(Flags::NONE) { |bitmask, privilege| FLAGS.key?(privilege.to_sym) ? (bitmask | FLAGS[privilege.to_sym]) : bitmask }
  107. end
  108. def can?(*any_of_privileges)
  109. any_of_privileges.any? { |privilege| in_permissions?(privilege) }
  110. end
  111. def overrides?(other_role)
  112. other_role.nil? || position > other_role.position
  113. end
  114. def computed_permissions
  115. # If called on the everyone role, no further computation needed
  116. return permissions if everyone?
  117. # If called on the nobody role, no permissions are there to be given
  118. return Flags::NONE if nobody?
  119. # Otherwise, compute permissions based on special conditions
  120. @computed_permissions ||= begin
  121. permissions = self.class.everyone.permissions | self.permissions
  122. if permissions & FLAGS[:administrator] == FLAGS[:administrator]
  123. Flags::ALL
  124. else
  125. permissions
  126. end
  127. end
  128. end
  129. def to_log_human_identifier
  130. name
  131. end
  132. private
  133. def in_permissions?(privilege)
  134. raise ArgumentError, "Unknown privilege: #{privilege}" unless FLAGS.key?(privilege)
  135. computed_permissions & FLAGS[privilege] == FLAGS[privilege]
  136. end
  137. def set_position
  138. self.position = -1 if everyone?
  139. end
  140. def validate_own_role_edition
  141. return unless defined?(@current_account) && @current_account.user_role.id == id
  142. errors.add(:permissions_as_keys, :own_role) if permissions_changed?
  143. errors.add(:position, :own_role) if position_changed?
  144. end
  145. def validate_permissions_elevation
  146. errors.add(:permissions_as_keys, :elevated) if defined?(@current_account) && @current_account.user_role.computed_permissions & permissions != permissions
  147. end
  148. def validate_position_elevation
  149. errors.add(:position, :elevated) if defined?(@current_account) && @current_account.user_role.position < position
  150. end
  151. def validate_dangerous_permissions
  152. errors.add(:permissions_as_keys, :dangerous) if everyone? && Flags::DEFAULT & permissions != permissions
  153. end
  154. end