user_role.rb 5.1 KB

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