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.

role.rb 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2019 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. class Role < ActiveRecord::Base
  19. include Redmine::SafeAttributes
  20. # Custom coder for the permissions attribute that should be an
  21. # array of symbols. Rails 3 uses Psych which can be *unbelievably*
  22. # slow on some platforms (eg. mingw32).
  23. class PermissionsAttributeCoder
  24. def self.load(str)
  25. str.to_s.scan(/:([a-z0-9_]+)/).flatten.map(&:to_sym)
  26. end
  27. def self.dump(value)
  28. YAML.dump(value)
  29. end
  30. end
  31. # Built-in roles
  32. BUILTIN_NON_MEMBER = 1
  33. BUILTIN_ANONYMOUS = 2
  34. ISSUES_VISIBILITY_OPTIONS = [
  35. ['all', :label_issues_visibility_all],
  36. ['default', :label_issues_visibility_public],
  37. ['own', :label_issues_visibility_own]
  38. ]
  39. TIME_ENTRIES_VISIBILITY_OPTIONS = [
  40. ['all', :label_time_entries_visibility_all],
  41. ['own', :label_time_entries_visibility_own]
  42. ]
  43. USERS_VISIBILITY_OPTIONS = [
  44. ['all', :label_users_visibility_all],
  45. ['members_of_visible_projects', :label_users_visibility_members_of_visible_projects]
  46. ]
  47. scope :sorted, lambda { order(:builtin, :position) }
  48. scope :givable, lambda { order(:position).where(:builtin => 0) }
  49. scope :builtin, lambda { |*args|
  50. compare = (args.first == true ? 'not' : '')
  51. where("#{compare} builtin = 0")
  52. }
  53. before_destroy :check_deletable
  54. has_many :workflow_rules, :dependent => :delete_all
  55. has_and_belongs_to_many :custom_fields, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "role_id"
  56. has_and_belongs_to_many :managed_roles, :class_name => 'Role',
  57. :join_table => "#{table_name_prefix}roles_managed_roles#{table_name_suffix}",
  58. :association_foreign_key => "managed_role_id"
  59. has_many :member_roles, :dependent => :destroy
  60. has_many :members, :through => :member_roles
  61. acts_as_positioned :scope => :builtin
  62. serialize :permissions, ::Role::PermissionsAttributeCoder
  63. store :settings, :accessors => [:permissions_all_trackers, :permissions_tracker_ids]
  64. validates_presence_of :name
  65. validates_uniqueness_of :name
  66. validates_length_of :name, :maximum => 255
  67. validates_inclusion_of :issues_visibility,
  68. :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
  69. :if => lambda {|role| role.respond_to?(:issues_visibility) && role.issues_visibility_changed?}
  70. validates_inclusion_of :users_visibility,
  71. :in => USERS_VISIBILITY_OPTIONS.collect(&:first),
  72. :if => lambda {|role| role.respond_to?(:users_visibility) && role.users_visibility_changed?}
  73. validates_inclusion_of :time_entries_visibility,
  74. :in => TIME_ENTRIES_VISIBILITY_OPTIONS.collect(&:first),
  75. :if => lambda {|role| role.respond_to?(:time_entries_visibility) && role.time_entries_visibility_changed?}
  76. safe_attributes 'name',
  77. 'assignable',
  78. 'position',
  79. 'issues_visibility',
  80. 'users_visibility',
  81. 'time_entries_visibility',
  82. 'all_roles_managed',
  83. 'managed_role_ids',
  84. 'permissions',
  85. 'permissions_all_trackers',
  86. 'permissions_tracker_ids'
  87. # Copies attributes from another role, arg can be an id or a Role
  88. def copy_from(arg, options={})
  89. return unless arg.present?
  90. role = arg.is_a?(Role) ? arg : Role.find_by_id(arg.to_s)
  91. self.attributes = role.attributes.dup.except("id", "name", "position", "builtin", "permissions")
  92. self.permissions = role.permissions.dup
  93. self.managed_role_ids = role.managed_role_ids.dup
  94. self
  95. end
  96. def permissions=(perms)
  97. perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
  98. write_attribute(:permissions, perms)
  99. end
  100. def add_permission!(*perms)
  101. self.permissions = [] unless permissions.is_a?(Array)
  102. permissions_will_change!
  103. perms.each do |p|
  104. p = p.to_sym
  105. permissions << p unless permissions.include?(p)
  106. end
  107. save!
  108. end
  109. def remove_permission!(*perms)
  110. return unless permissions.is_a?(Array)
  111. permissions_will_change!
  112. perms.each { |p| permissions.delete(p.to_sym) }
  113. save!
  114. end
  115. # Returns true if the role has the given permission
  116. def has_permission?(perm)
  117. !permissions.nil? && permissions.include?(perm.to_sym)
  118. end
  119. def consider_workflow?
  120. has_permission?(:add_issues) || has_permission?(:edit_issues)
  121. end
  122. def <=>(role)
  123. if role
  124. if builtin == role.builtin
  125. position <=> role.position
  126. else
  127. builtin <=> role.builtin
  128. end
  129. else
  130. -1
  131. end
  132. end
  133. def to_s
  134. name
  135. end
  136. def name
  137. case builtin
  138. when 1; l(:label_role_non_member, :default => read_attribute(:name))
  139. when 2; l(:label_role_anonymous, :default => read_attribute(:name))
  140. else; read_attribute(:name)
  141. end
  142. end
  143. # Return true if the role is a builtin role
  144. def builtin?
  145. self.builtin != 0
  146. end
  147. # Return true if the role is the anonymous role
  148. def anonymous?
  149. builtin == 2
  150. end
  151. # Return true if the role is a project member role
  152. def member?
  153. !self.builtin?
  154. end
  155. # Return true if role is allowed to do the specified action
  156. # action can be:
  157. # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
  158. # * a permission Symbol (eg. :edit_project)
  159. def allowed_to?(action)
  160. if action.is_a? Hash
  161. allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
  162. else
  163. allowed_permissions.include? action
  164. end
  165. end
  166. # Return all the permissions that can be given to the role
  167. def setable_permissions
  168. setable_permissions = Redmine::AccessControl.permissions - Redmine::AccessControl.public_permissions
  169. setable_permissions -= Redmine::AccessControl.members_only_permissions if self.builtin == BUILTIN_NON_MEMBER
  170. setable_permissions -= Redmine::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
  171. setable_permissions
  172. end
  173. def permissions_tracker_ids(*args)
  174. if args.any?
  175. Array(permissions_tracker_ids[args.first.to_s]).map(&:to_i)
  176. else
  177. super || {}
  178. end
  179. end
  180. def permissions_tracker_ids=(arg)
  181. h = arg.to_hash
  182. h.values.each {|v| v.reject!(&:blank?)}
  183. super(h)
  184. end
  185. # Returns true if tracker_id belongs to the list of
  186. # trackers for which permission is given
  187. def permissions_tracker_ids?(permission, tracker_id)
  188. permissions_tracker_ids(permission).include?(tracker_id)
  189. end
  190. def permissions_all_trackers
  191. super || {}
  192. end
  193. def permissions_all_trackers=(arg)
  194. super(arg.to_hash)
  195. end
  196. # Returns true if permission is given for all trackers
  197. def permissions_all_trackers?(permission)
  198. permissions_all_trackers[permission.to_s].to_s != '0'
  199. end
  200. # Returns true if permission is given for the tracker
  201. # (explicitly or for all trackers)
  202. def permissions_tracker?(permission, tracker)
  203. permissions_all_trackers?(permission) ||
  204. permissions_tracker_ids?(permission, tracker.try(:id))
  205. end
  206. # Sets the trackers that are allowed for a permission.
  207. # tracker_ids can be an array of tracker ids or :all for
  208. # no restrictions.
  209. #
  210. # Examples:
  211. # role.set_permission_trackers :add_issues, [1, 3]
  212. # role.set_permission_trackers :add_issues, :all
  213. def set_permission_trackers(permission, tracker_ids)
  214. h = {permission.to_s => (tracker_ids == :all ? '1' : '0')}
  215. self.permissions_all_trackers = permissions_all_trackers.merge(h)
  216. h = {permission.to_s => (tracker_ids == :all ? [] : tracker_ids)}
  217. self.permissions_tracker_ids = permissions_tracker_ids.merge(h)
  218. self
  219. end
  220. def copy_workflow_rules(source_role)
  221. WorkflowRule.copy(nil, source_role, nil, self)
  222. end
  223. # Find all the roles that can be given to a project member
  224. def self.find_all_givable
  225. Role.givable.to_a
  226. end
  227. # Return the builtin 'non member' role. If the role doesn't exist,
  228. # it will be created on the fly.
  229. def self.non_member
  230. find_or_create_system_role(BUILTIN_NON_MEMBER, 'Non member')
  231. end
  232. # Return the builtin 'anonymous' role. If the role doesn't exist,
  233. # it will be created on the fly.
  234. def self.anonymous
  235. find_or_create_system_role(BUILTIN_ANONYMOUS, 'Anonymous')
  236. end
  237. private
  238. def allowed_permissions
  239. @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name}
  240. end
  241. def allowed_actions
  242. @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
  243. end
  244. def check_deletable
  245. raise "Cannot delete role" if members.any?
  246. raise "Cannot delete builtin role" if builtin?
  247. end
  248. def self.find_or_create_system_role(builtin, name)
  249. role = unscoped.find_by(:builtin => builtin)
  250. if role.nil?
  251. role = unscoped.create(:name => name) do |r|
  252. r.builtin = builtin
  253. end
  254. raise "Unable to create the #{name} role (#{role.errors.full_messages.join(',')})." if role.new_record?
  255. end
  256. role
  257. end
  258. end