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

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