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.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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, :case_sensitive => false
  66. validates_length_of :name, :maximum => 255
  67. validates_inclusion_of(
  68. :issues_visibility,
  69. :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
  70. :if => lambda {|role| role.respond_to?(:issues_visibility) && role.issues_visibility_changed?})
  71. validates_inclusion_of(
  72. :users_visibility,
  73. :in => USERS_VISIBILITY_OPTIONS.collect(&:first),
  74. :if => lambda {|role| role.respond_to?(:users_visibility) && role.users_visibility_changed?})
  75. validates_inclusion_of(
  76. :time_entries_visibility,
  77. :in => TIME_ENTRIES_VISIBILITY_OPTIONS.collect(&:first),
  78. :if => lambda {|role| role.respond_to?(:time_entries_visibility) && role.time_entries_visibility_changed?})
  79. safe_attributes(
  80. 'name',
  81. 'assignable',
  82. 'position',
  83. 'issues_visibility',
  84. 'users_visibility',
  85. 'time_entries_visibility',
  86. 'all_roles_managed',
  87. 'managed_role_ids',
  88. 'permissions',
  89. 'permissions_all_trackers',
  90. 'permissions_tracker_ids')
  91. # Copies attributes from another role, arg can be an id or a Role
  92. def copy_from(arg, options={})
  93. return unless arg.present?
  94. role = arg.is_a?(Role) ? arg : Role.find_by_id(arg.to_s)
  95. self.attributes = role.attributes.dup.except("id", "name", "position", "builtin", "permissions")
  96. self.permissions = role.permissions.dup
  97. self.managed_role_ids = role.managed_role_ids.dup
  98. self
  99. end
  100. def permissions=(perms)
  101. perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
  102. write_attribute(:permissions, perms)
  103. end
  104. def add_permission!(*perms)
  105. self.permissions = [] unless permissions.is_a?(Array)
  106. permissions_will_change!
  107. perms.each do |p|
  108. p = p.to_sym
  109. permissions << p unless permissions.include?(p)
  110. end
  111. save!
  112. end
  113. def remove_permission!(*perms)
  114. return unless permissions.is_a?(Array)
  115. permissions_will_change!
  116. perms.each { |p| permissions.delete(p.to_sym) }
  117. save!
  118. end
  119. # Returns true if the role has the given permission
  120. def has_permission?(perm)
  121. !permissions.nil? && permissions.include?(perm.to_sym)
  122. end
  123. def consider_workflow?
  124. has_permission?(:add_issues) || has_permission?(:edit_issues)
  125. end
  126. def <=>(role)
  127. if role
  128. if builtin == role.builtin
  129. position <=> role.position
  130. else
  131. builtin <=> role.builtin
  132. end
  133. else
  134. -1
  135. end
  136. end
  137. def to_s
  138. name
  139. end
  140. def name
  141. case builtin
  142. when 1 then l(:label_role_non_member, :default => read_attribute(:name))
  143. when 2 then l(:label_role_anonymous, :default => read_attribute(:name))
  144. else
  145. read_attribute(:name)
  146. end
  147. end
  148. # Return true if the role is a builtin role
  149. def builtin?
  150. self.builtin != 0
  151. end
  152. # Return true if the role is the anonymous role
  153. def anonymous?
  154. builtin == 2
  155. end
  156. # Return true if the role is a project member role
  157. def member?
  158. !self.builtin?
  159. end
  160. # Return true if role is allowed to do the specified action
  161. # action can be:
  162. # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
  163. # * a permission Symbol (eg. :edit_project)
  164. def allowed_to?(action)
  165. if action.is_a? Hash
  166. allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
  167. else
  168. allowed_permissions.include? action
  169. end
  170. end
  171. # Return all the permissions that can be given to the role
  172. def setable_permissions
  173. setable_permissions = Redmine::AccessControl.permissions - Redmine::AccessControl.public_permissions
  174. setable_permissions -= Redmine::AccessControl.members_only_permissions if self.builtin == BUILTIN_NON_MEMBER
  175. setable_permissions -= Redmine::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
  176. setable_permissions
  177. end
  178. def permissions_tracker_ids(*args)
  179. if args.any?
  180. Array(permissions_tracker_ids[args.first.to_s]).map(&:to_i)
  181. else
  182. super || {}
  183. end
  184. end
  185. def permissions_tracker_ids=(arg)
  186. h = arg.to_hash
  187. h.values.each {|v| v.reject!(&:blank?)}
  188. super(h)
  189. end
  190. # Returns true if tracker_id belongs to the list of
  191. # trackers for which permission is given
  192. def permissions_tracker_ids?(permission, tracker_id)
  193. permissions_tracker_ids(permission).include?(tracker_id)
  194. end
  195. def permissions_all_trackers
  196. super || {}
  197. end
  198. def permissions_all_trackers=(arg)
  199. super(arg.to_hash)
  200. end
  201. # Returns true if permission is given for all trackers
  202. def permissions_all_trackers?(permission)
  203. permissions_all_trackers[permission.to_s].to_s != '0'
  204. end
  205. # Returns true if permission is given for the tracker
  206. # (explicitly or for all trackers)
  207. def permissions_tracker?(permission, tracker)
  208. permissions_all_trackers?(permission) ||
  209. permissions_tracker_ids?(permission, tracker.try(:id))
  210. end
  211. # Sets the trackers that are allowed for a permission.
  212. # tracker_ids can be an array of tracker ids or :all for
  213. # no restrictions.
  214. #
  215. # Examples:
  216. # role.set_permission_trackers :add_issues, [1, 3]
  217. # role.set_permission_trackers :add_issues, :all
  218. def set_permission_trackers(permission, tracker_ids)
  219. h = {permission.to_s => (tracker_ids == :all ? '1' : '0')}
  220. self.permissions_all_trackers = permissions_all_trackers.merge(h)
  221. h = {permission.to_s => (tracker_ids == :all ? [] : tracker_ids)}
  222. self.permissions_tracker_ids = permissions_tracker_ids.merge(h)
  223. self
  224. end
  225. def copy_workflow_rules(source_role)
  226. WorkflowRule.copy(nil, source_role, nil, self)
  227. end
  228. # Find all the roles that can be given to a project member
  229. def self.find_all_givable
  230. Role.givable.to_a
  231. end
  232. # Return the builtin 'non member' role. If the role doesn't exist,
  233. # it will be created on the fly.
  234. def self.non_member
  235. find_or_create_system_role(BUILTIN_NON_MEMBER, 'Non member')
  236. end
  237. # Return the builtin 'anonymous' role. If the role doesn't exist,
  238. # it will be created on the fly.
  239. def self.anonymous
  240. find_or_create_system_role(BUILTIN_ANONYMOUS, 'Anonymous')
  241. end
  242. private
  243. def allowed_permissions
  244. @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name}
  245. end
  246. def allowed_actions
  247. @actions_allowed ||=
  248. allowed_permissions.inject([]) {|actions, permission|
  249. actions += Redmine::AccessControl.allowed_actions(permission)
  250. }.flatten
  251. end
  252. def check_deletable
  253. raise "Cannot delete role" if members.any?
  254. raise "Cannot delete builtin role" if builtin?
  255. end
  256. def self.find_or_create_system_role(builtin, name)
  257. role = unscoped.find_by(:builtin => builtin)
  258. if role.nil?
  259. role = unscoped.create(:name => name) do |r|
  260. r.builtin = builtin
  261. end
  262. raise "Unable to create the #{name} role (#{role.errors.full_messages.join(',')})." if role.new_record?
  263. end
  264. role
  265. end
  266. private_class_method :find_or_create_system_role
  267. end