##// END OF EJS Templates
Fixed that r15668 prevents from setting managed roles....
Jean-Philippe Lang -
r15530:a2b17dae6e89
parent child
Show More
@@ -1,304 +1,305
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Role < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 20
21 21 # Custom coder for the permissions attribute that should be an
22 22 # array of symbols. Rails 3 uses Psych which can be *unbelievably*
23 23 # slow on some platforms (eg. mingw32).
24 24 class PermissionsAttributeCoder
25 25 def self.load(str)
26 26 str.to_s.scan(/:([a-z0-9_]+)/).flatten.map(&:to_sym)
27 27 end
28 28
29 29 def self.dump(value)
30 30 YAML.dump(value)
31 31 end
32 32 end
33 33
34 34 # Built-in roles
35 35 BUILTIN_NON_MEMBER = 1
36 36 BUILTIN_ANONYMOUS = 2
37 37
38 38 ISSUES_VISIBILITY_OPTIONS = [
39 39 ['all', :label_issues_visibility_all],
40 40 ['default', :label_issues_visibility_public],
41 41 ['own', :label_issues_visibility_own]
42 42 ]
43 43
44 44 TIME_ENTRIES_VISIBILITY_OPTIONS = [
45 45 ['all', :label_time_entries_visibility_all],
46 46 ['own', :label_time_entries_visibility_own]
47 47 ]
48 48
49 49 USERS_VISIBILITY_OPTIONS = [
50 50 ['all', :label_users_visibility_all],
51 51 ['members_of_visible_projects', :label_users_visibility_members_of_visible_projects]
52 52 ]
53 53
54 54 scope :sorted, lambda { order(:builtin, :position) }
55 55 scope :givable, lambda { order(:position).where(:builtin => 0) }
56 56 scope :builtin, lambda { |*args|
57 57 compare = (args.first == true ? 'not' : '')
58 58 where("#{compare} builtin = 0")
59 59 }
60 60
61 61 before_destroy :check_deletable
62 62 has_many :workflow_rules, :dependent => :delete_all do
63 63 def copy(source_role)
64 64 WorkflowRule.copy(nil, source_role, nil, proxy_association.owner)
65 65 end
66 66 end
67 67 has_and_belongs_to_many :custom_fields, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "role_id"
68 68
69 69 has_and_belongs_to_many :managed_roles, :class_name => 'Role',
70 70 :join_table => "#{table_name_prefix}roles_managed_roles#{table_name_suffix}",
71 71 :association_foreign_key => "managed_role_id"
72 72
73 73 has_many :member_roles, :dependent => :destroy
74 74 has_many :members, :through => :member_roles
75 75 acts_as_positioned :scope => :builtin
76 76
77 77 serialize :permissions, ::Role::PermissionsAttributeCoder
78 78 store :settings, :accessors => [:permissions_all_trackers, :permissions_tracker_ids]
79 79 attr_protected :builtin
80 80
81 81 validates_presence_of :name
82 82 validates_uniqueness_of :name
83 83 validates_length_of :name, :maximum => 30
84 84 validates_inclusion_of :issues_visibility,
85 85 :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
86 86 :if => lambda {|role| role.respond_to?(:issues_visibility) && role.issues_visibility_changed?}
87 87 validates_inclusion_of :users_visibility,
88 88 :in => USERS_VISIBILITY_OPTIONS.collect(&:first),
89 89 :if => lambda {|role| role.respond_to?(:users_visibility) && role.users_visibility_changed?}
90 90 validates_inclusion_of :time_entries_visibility,
91 91 :in => TIME_ENTRIES_VISIBILITY_OPTIONS.collect(&:first),
92 92 :if => lambda {|role| role.respond_to?(:time_entries_visibility) && role.time_entries_visibility_changed?}
93 93
94 94 safe_attributes 'name',
95 95 'assignable',
96 96 'position',
97 97 'issues_visibility',
98 98 'users_visibility',
99 99 'time_entries_visibility',
100 100 'all_roles_managed',
101 'managed_role_ids',
101 102 'permissions',
102 103 'permissions_all_trackers',
103 104 'permissions_tracker_ids'
104 105
105 106 # Copies attributes from another role, arg can be an id or a Role
106 107 def copy_from(arg, options={})
107 108 return unless arg.present?
108 109 role = arg.is_a?(Role) ? arg : Role.find_by_id(arg.to_s)
109 110 self.attributes = role.attributes.dup.except("id", "name", "position", "builtin", "permissions")
110 111 self.permissions = role.permissions.dup
111 112 self
112 113 end
113 114
114 115 def permissions=(perms)
115 116 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
116 117 write_attribute(:permissions, perms)
117 118 end
118 119
119 120 def add_permission!(*perms)
120 121 self.permissions = [] unless permissions.is_a?(Array)
121 122
122 123 permissions_will_change!
123 124 perms.each do |p|
124 125 p = p.to_sym
125 126 permissions << p unless permissions.include?(p)
126 127 end
127 128 save!
128 129 end
129 130
130 131 def remove_permission!(*perms)
131 132 return unless permissions.is_a?(Array)
132 133 permissions_will_change!
133 134 perms.each { |p| permissions.delete(p.to_sym) }
134 135 save!
135 136 end
136 137
137 138 # Returns true if the role has the given permission
138 139 def has_permission?(perm)
139 140 !permissions.nil? && permissions.include?(perm.to_sym)
140 141 end
141 142
142 143 def consider_workflow?
143 144 has_permission?(:add_issues) || has_permission?(:edit_issues)
144 145 end
145 146
146 147 def <=>(role)
147 148 if role
148 149 if builtin == role.builtin
149 150 position <=> role.position
150 151 else
151 152 builtin <=> role.builtin
152 153 end
153 154 else
154 155 -1
155 156 end
156 157 end
157 158
158 159 def to_s
159 160 name
160 161 end
161 162
162 163 def name
163 164 case builtin
164 165 when 1; l(:label_role_non_member, :default => read_attribute(:name))
165 166 when 2; l(:label_role_anonymous, :default => read_attribute(:name))
166 167 else; read_attribute(:name)
167 168 end
168 169 end
169 170
170 171 # Return true if the role is a builtin role
171 172 def builtin?
172 173 self.builtin != 0
173 174 end
174 175
175 176 # Return true if the role is the anonymous role
176 177 def anonymous?
177 178 builtin == 2
178 179 end
179 180
180 181 # Return true if the role is a project member role
181 182 def member?
182 183 !self.builtin?
183 184 end
184 185
185 186 # Return true if role is allowed to do the specified action
186 187 # action can be:
187 188 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
188 189 # * a permission Symbol (eg. :edit_project)
189 190 def allowed_to?(action)
190 191 if action.is_a? Hash
191 192 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
192 193 else
193 194 allowed_permissions.include? action
194 195 end
195 196 end
196 197
197 198 # Return all the permissions that can be given to the role
198 199 def setable_permissions
199 200 setable_permissions = Redmine::AccessControl.permissions - Redmine::AccessControl.public_permissions
200 201 setable_permissions -= Redmine::AccessControl.members_only_permissions if self.builtin == BUILTIN_NON_MEMBER
201 202 setable_permissions -= Redmine::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
202 203 setable_permissions
203 204 end
204 205
205 206 def permissions_tracker_ids(*args)
206 207 if args.any?
207 208 Array(permissions_tracker_ids[args.first.to_s]).map(&:to_i)
208 209 else
209 210 super || {}
210 211 end
211 212 end
212 213
213 214 def permissions_tracker_ids=(arg)
214 215 h = arg.to_hash
215 216 h.values.each {|v| v.reject!(&:blank?)}
216 217 super(h)
217 218 end
218 219
219 220 # Returns true if tracker_id belongs to the list of
220 221 # trackers for which permission is given
221 222 def permissions_tracker_ids?(permission, tracker_id)
222 223 permissions_tracker_ids(permission).include?(tracker_id)
223 224 end
224 225
225 226 def permissions_all_trackers
226 227 super || {}
227 228 end
228 229
229 230 def permissions_all_trackers=(arg)
230 231 super(arg.to_hash)
231 232 end
232 233
233 234 # Returns true if permission is given for all trackers
234 235 def permissions_all_trackers?(permission)
235 236 permissions_all_trackers[permission.to_s].to_s != '0'
236 237 end
237 238
238 239 # Returns true if permission is given for the tracker
239 240 # (explicitly or for all trackers)
240 241 def permissions_tracker?(permission, tracker)
241 242 permissions_all_trackers?(permission) ||
242 243 permissions_tracker_ids?(permission, tracker.try(:id))
243 244 end
244 245
245 246 # Sets the trackers that are allowed for a permission.
246 247 # tracker_ids can be an array of tracker ids or :all for
247 248 # no restrictions.
248 249 #
249 250 # Examples:
250 251 # role.set_permission_trackers :add_issues, [1, 3]
251 252 # role.set_permission_trackers :add_issues, :all
252 253 def set_permission_trackers(permission, tracker_ids)
253 254 h = {permission.to_s => (tracker_ids == :all ? '1' : '0')}
254 255 self.permissions_all_trackers = permissions_all_trackers.merge(h)
255 256
256 257 h = {permission.to_s => (tracker_ids == :all ? [] : tracker_ids)}
257 258 self.permissions_tracker_ids = permissions_tracker_ids.merge(h)
258 259
259 260 self
260 261 end
261 262
262 263 # Find all the roles that can be given to a project member
263 264 def self.find_all_givable
264 265 Role.givable.to_a
265 266 end
266 267
267 268 # Return the builtin 'non member' role. If the role doesn't exist,
268 269 # it will be created on the fly.
269 270 def self.non_member
270 271 find_or_create_system_role(BUILTIN_NON_MEMBER, 'Non member')
271 272 end
272 273
273 274 # Return the builtin 'anonymous' role. If the role doesn't exist,
274 275 # it will be created on the fly.
275 276 def self.anonymous
276 277 find_or_create_system_role(BUILTIN_ANONYMOUS, 'Anonymous')
277 278 end
278 279
279 280 private
280 281
281 282 def allowed_permissions
282 283 @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name}
283 284 end
284 285
285 286 def allowed_actions
286 287 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
287 288 end
288 289
289 290 def check_deletable
290 291 raise "Cannot delete role" if members.any?
291 292 raise "Cannot delete builtin role" if builtin?
292 293 end
293 294
294 295 def self.find_or_create_system_role(builtin, name)
295 296 role = where(:builtin => builtin).first
296 297 if role.nil?
297 298 role = create(:name => name) do |r|
298 299 r.builtin = builtin
299 300 end
300 301 raise "Unable to create the #{name} role (#{role.errors.full_messages.join(',')})." if role.new_record?
301 302 end
302 303 role
303 304 end
304 305 end
@@ -1,248 +1,263
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RolesControllerTest < Redmine::ControllerTest
21 21 fixtures :roles, :users, :members, :member_roles, :workflows, :trackers
22 22
23 23 def setup
24 24 User.current = nil
25 25 @request.session[:user_id] = 1 # admin
26 26 end
27 27
28 28 def test_index
29 29 get :index
30 30 assert_response :success
31 31
32 32 assert_select 'table.roles tbody' do
33 33 assert_select 'tr', Role.count
34 34 assert_select 'a[href="/roles/1/edit"]', :text => 'Manager'
35 35 end
36 36 end
37 37
38 38 def test_new
39 39 get :new
40 40 assert_response :success
41 41 assert_select 'input[name=?]', 'role[name]'
42 42 assert_select 'input[name=?]', 'role[permissions][]'
43 43 end
44 44
45 45 def test_new_should_prefill_permissions_with_non_member_permissions
46 46 role = Role.non_member
47 47 role.permissions = [:view_issues, :view_documents]
48 48 role.save!
49 49
50 50 get :new
51 51 assert_response :success
52 52 assert_equal %w(view_documents view_issues),
53 53 css_select('input[name="role[permissions][]"][checked=checked]').map {|e| e.attr('value')}.sort
54 54 end
55 55
56 56 def test_new_with_copy
57 57 copy_from = Role.find(2)
58 58
59 59 get :new, :params => {:copy => copy_from.id.to_s}
60 60 assert_response :success
61 61 assert_select 'input[name=?]', 'role[name]'
62 62
63 63 assert_select 'form' do
64 64 # blank name
65 65 assert_select 'input[name=?][value=""]', 'role[name]'
66 66 # edit_project permission checked
67 67 assert_select 'input[type=checkbox][name=?][value=edit_project][checked=checked]', 'role[permissions][]'
68 68 # add_project permission not checked
69 69 assert_select 'input[type=checkbox][name=?][value=add_project]', 'role[permissions][]'
70 70 assert_select 'input[type=checkbox][name=?][value=add_project][checked=checked]', 'role[permissions][]', 0
71 71 # workflow copy selected
72 72 assert_select 'select[name=?]', 'copy_workflow_from' do
73 73 assert_select 'option[value="2"][selected=selected]'
74 74 end
75 75 end
76 76 end
77 77
78 78 def test_create_with_validaton_failure
79 79 post :create, :params => {
80 80 :role => {
81 81 :name => '',
82 82 :permissions => ['add_issues', 'edit_issues', 'log_time', ''],
83 83 :assignable => '0'
84 84 }
85 85 }
86 86 assert_response :success
87 87 assert_select_error /Name cannot be blank/
88 88 end
89 89
90 90 def test_create_without_workflow_copy
91 91 post :create, :params => {
92 92 :role => {
93 93 :name => 'RoleWithoutWorkflowCopy',
94 94 :permissions => ['add_issues', 'edit_issues', 'log_time', ''],
95 95 :assignable => '0'
96 96 }
97 97 }
98 98 assert_redirected_to '/roles'
99 99 role = Role.find_by_name('RoleWithoutWorkflowCopy')
100 100 assert_not_nil role
101 101 assert_equal [:add_issues, :edit_issues, :log_time], role.permissions
102 102 assert !role.assignable?
103 103 end
104 104
105 105 def test_create_with_workflow_copy
106 106 post :create, :params => {
107 107 :role => {
108 108 :name => 'RoleWithWorkflowCopy',
109 109 :permissions => ['add_issues', 'edit_issues', 'log_time', ''],
110 110 :assignable => '0'
111 111 },
112 112 :copy_workflow_from => '1'
113 113 }
114 114 assert_redirected_to '/roles'
115 115 role = Role.find_by_name('RoleWithWorkflowCopy')
116 116 assert_not_nil role
117 117 assert_equal Role.find(1).workflow_rules.size, role.workflow_rules.size
118 118 end
119 119
120 def test_create_with_managed_roles
121 role = new_record(Role) do
122 post :create, :params => {
123 :role => {
124 :name => 'Role',
125 :all_roles_managed => '0',
126 :managed_role_ids => ['2', '3', '']
127 }
128 }
129 assert_response 302
130 end
131 assert_equal false, role.all_roles_managed
132 assert_equal [2, 3], role.managed_role_ids
133 end
134
120 135 def test_edit
121 136 get :edit, :params => {:id => 1}
122 137 assert_response :success
123 138
124 139 assert_select 'input[name=?][value=?]', 'role[name]', 'Manager'
125 140 assert_select 'select[name=?]', 'role[issues_visibility]'
126 141 end
127 142
128 143 def test_edit_anonymous
129 144 get :edit, :params => {:id => Role.anonymous.id}
130 145 assert_response :success
131 146
132 147 assert_select 'input[name=?]', 'role[name]', 0
133 148 assert_select 'select[name=?]', 'role[issues_visibility]', 0
134 149 end
135 150
136 151 def test_edit_invalid_should_respond_with_404
137 152 get :edit, :params => {:id => 999}
138 153 assert_response 404
139 154 end
140 155
141 156 def test_update
142 157 put :update, :params => {
143 158 :id => 1,
144 159 :role => {
145 160 :name => 'Manager',
146 161 :permissions => ['edit_project', ''],
147 162 :assignable => '0'
148 163 }
149 164 }
150 165 assert_redirected_to '/roles'
151 166 role = Role.find(1)
152 167 assert_equal [:edit_project], role.permissions
153 168 end
154 169
155 170 def test_update_trackers_permissions
156 171 put :update, :params => {
157 172 :id => 1,
158 173 :role => {
159 174 :permissions_all_trackers => {'add_issues' => '0'},
160 175 :permissions_tracker_ids => {'add_issues' => ['1', '3', '']}
161 176 }
162 177 }
163 178 assert_redirected_to '/roles'
164 179 role = Role.find(1)
165 180
166 181 assert_equal({'add_issues' => '0'}, role.permissions_all_trackers)
167 182 assert_equal({'add_issues' => ['1', '3']}, role.permissions_tracker_ids)
168 183
169 184 assert_equal false, role.permissions_all_trackers?(:add_issues)
170 185 assert_equal [1, 3], role.permissions_tracker_ids(:add_issues).sort
171 186 end
172 187
173 188 def test_update_with_failure
174 189 put :update, :params => {:id => 1, :role => {:name => ''}}
175 190 assert_response :success
176 191 assert_select_error /Name cannot be blank/
177 192 end
178 193
179 194 def test_destroy
180 195 r = Role.create!(:name => 'ToBeDestroyed', :permissions => [:view_wiki_pages])
181 196
182 197 delete :destroy, :params => {:id => r}
183 198 assert_redirected_to '/roles'
184 199 assert_nil Role.find_by_id(r.id)
185 200 end
186 201
187 202 def test_destroy_role_in_use
188 203 delete :destroy, :params => {:id => 1}
189 204 assert_redirected_to '/roles'
190 205 assert_equal 'This role is in use and cannot be deleted.', flash[:error]
191 206 assert_not_nil Role.find_by_id(1)
192 207 end
193 208
194 209 def test_get_permissions
195 210 get :permissions
196 211 assert_response :success
197 212
198 213 assert_select 'input[name=?][type=checkbox][value=add_issues][checked=checked]', 'permissions[3][]'
199 214 assert_select 'input[name=?][type=checkbox][value=delete_issues]:not([checked])', 'permissions[3][]'
200 215 end
201 216
202 217 def test_post_permissions
203 218 post :permissions, :params => {
204 219 :permissions => {
205 220 '0' => '',
206 221 '1' => ['edit_issues'],
207 222 '3' => ['add_issues', 'delete_issues']
208 223 }
209 224 }
210 225 assert_redirected_to '/roles'
211 226
212 227 assert_equal [:edit_issues], Role.find(1).permissions
213 228 assert_equal [:add_issues, :delete_issues], Role.find(3).permissions
214 229 assert Role.find(2).permissions.empty?
215 230 end
216 231
217 232 def test_clear_all_permissions
218 233 post :permissions, :params => {:permissions => { '0' => '' }}
219 234 assert_redirected_to '/roles'
220 235 assert Role.find(1).permissions.empty?
221 236 end
222 237
223 238 def test_move_highest
224 239 put :update, :params => {:id => 3, :role => {:position => 1}}
225 240 assert_redirected_to '/roles'
226 241 assert_equal 1, Role.find(3).position
227 242 end
228 243
229 244 def test_move_higher
230 245 position = Role.find(3).position
231 246 put :update, :params => {:id => 3, :role => {:position => position - 1}}
232 247 assert_redirected_to '/roles'
233 248 assert_equal position - 1, Role.find(3).position
234 249 end
235 250
236 251 def test_move_lower
237 252 position = Role.find(2).position
238 253 put :update, :params => {:id => 2, :role => {:position => position + 1}}
239 254 assert_redirected_to '/roles'
240 255 assert_equal position + 1, Role.find(2).position
241 256 end
242 257
243 258 def test_move_lowest
244 259 put :update, :params => {:id => 2, :role => {:position => Role.givable.count}}
245 260 assert_redirected_to '/roles'
246 261 assert_equal Role.givable.count, Role.find(2).position
247 262 end
248 263 end
General Comments 0
You need to be logged in to leave comments. Login now