##// END OF EJS Templates
Adds buit-in groups to give specific permissions to anonymous and non members users per project (#17976)....
Jean-Philippe Lang -
r13053:7e7ac5340a28
parent child
Show More
@@ -0,0 +1,30
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 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
18 class GroupAnonymous < GroupBuiltin
19 def name
20 l(:label_group_anonymous)
21 end
22
23 def builtin_type
24 "anonymous"
25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 end
@@ -0,0 +1,56
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 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
18 class GroupBuiltin < Group
19 validate :validate_uniqueness, :on => :create
20
21 def validate_uniqueness
22 errors.add :base, 'The builtin group already exists.' if self.class.exists?
23 end
24
25 def builtin?
26 true
27 end
28
29 def destroy
30 false
31 end
32
33 def user_added(user)
34 raise 'Cannot add users to a builtin group'
35 end
36
37 class << self
38 def load_instance
39 return nil if self == GroupBuiltin
40 instance = first(:order => 'id') || create_instance
41 end
42
43 def create_instance
44 raise 'The builtin group already exists.' if exists?
45 instance = new
46 instance.lastname = name
47 instance.save :validate => false
48 raise 'Unable to create builtin group.' if instance.new_record?
49 instance
50 end
51 private :create_instance
52 end
53 end
54
55 require_dependency "group_anonymous"
56 require_dependency "group_non_member"
@@ -0,0 +1,30
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 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
18 class GroupNonMember < GroupBuiltin
19 def name
20 l(:label_group_non_member)
21 end
22
23 def builtin_type
24 "non_member"
25 end
26
27 def self.instance_id
28 @@instance_id ||= load_instance.id
29 end
30 end
@@ -0,0 +1,21
1 class InsertBuiltinGroups < ActiveRecord::Migration
2 def up
3 Group.reset_column_information
4
5 unless GroupAnonymous.any?
6 g = GroupAnonymous.new(:lastname => 'Anonymous users')
7 g.status = 1
8 g.save :validate => false
9 end
10 unless GroupNonMember.any?
11 g = GroupNonMember.new(:lastname => 'Non member users')
12 g.status = 1
13 g.save :validate => false
14 end
15 end
16
17 def down
18 GroupAnonymous.delete_all
19 GroupNonMember.delete_all
20 end
21 end
@@ -25,12 +25,16 class GroupsController < ApplicationController
25 25 helper :custom_fields
26 26
27 27 def index
28 @groups = Group.sorted.all
29 28 respond_to do |format|
30 29 format.html {
30 @groups = Group.sorted.all
31 31 @user_count_by_group_id = user_count_by_group_id
32 32 }
33 format.api
33 format.api {
34 scope = Group.sorted
35 scope = scope.givable unless params[:builtin] == '1'
36 @groups = scope.all
37 }
34 38 end
35 39 end
36 40
@@ -18,11 +18,12
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module GroupsHelper
21 def group_settings_tabs
22 tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
23 {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
24 {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
25 ]
21 def group_settings_tabs(group)
22 tabs = []
23 tabs << {:name => 'general', :partial => 'groups/general', :label => :label_general}
24 tabs << {:name => 'users', :partial => 'groups/users', :label => :label_user_plural} if group.givable?
25 tabs << {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
26 tabs
26 27 end
27 28
28 29 def render_principals_for_new_group_users(group)
@@ -46,7 +46,7 module UsersHelper
46 46 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
47 47 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
48 48 ]
49 if Group.all.any?
49 if Group.givable.any?
50 50 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
51 51 end
52 52 tabs
@@ -31,17 +31,18 class Group < Principal
31 31
32 32 before_destroy :remove_references_before_destroy
33 33
34 scope :sorted, lambda { order("#{table_name}.lastname ASC") }
34 scope :sorted, lambda { order("#{table_name}.type, #{table_name}.lastname ASC") }
35 35 scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)}
36 scope :givable, lambda {where(:type => 'Group')}
36 37
37 38 safe_attributes 'name',
38 39 'user_ids',
39 40 'custom_field_values',
40 41 'custom_fields',
41 :if => lambda {|group, user| user.admin?}
42 :if => lambda {|group, user| user.admin? && !group.builtin?}
42 43
43 44 def to_s
44 lastname.to_s
45 name.to_s
45 46 end
46 47
47 48 def name
@@ -52,6 +53,20 class Group < Principal
52 53 self.lastname = arg
53 54 end
54 55
56 def builtin_type
57 nil
58 end
59
60 # Return true if the group is a builtin group
61 def builtin?
62 false
63 end
64
65 # Returns true if the group can be given to a user
66 def givable?
67 !builtin?
68 end
69
55 70 def user_added(user)
56 71 members.each do |member|
57 72 next if member.project.nil?
@@ -80,6 +95,18 class Group < Principal
80 95 super(attr_name, *args)
81 96 end
82 97
98 def self.builtin_id(arg)
99 (arg.anonymous? ? GroupAnonymous : GroupNonMember).instance_id
100 end
101
102 def self.anonymous
103 GroupAnonymous.load_instance
104 end
105
106 def self.non_member
107 GroupNonMember.load_instance
108 end
109
83 110 private
84 111
85 112 # Removes references that are not handled by associations
@@ -89,3 +116,5 class Group < Principal
89 116 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
90 117 end
91 118 end
119
120 require_dependency "group_builtin"
@@ -147,6 +147,7 class IssueQuery < Query
147 147 end
148 148 principals.uniq!
149 149 principals.sort!
150 principals.reject! {|p| p.is_a?(GroupBuiltin)}
150 151 users = principals.select {|p| p.is_a?(User)}
151 152
152 153 add_available_filter "status_id",
@@ -183,7 +184,7 class IssueQuery < Query
183 184 :type => :list_optional, :values => assigned_to_values
184 185 ) unless assigned_to_values.empty?
185 186
186 group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
187 group_values = Group.givable.collect {|g| [g.name, g.id.to_s] }
187 188 add_available_filter("member_of_group",
188 189 :type => :list_optional, :values => group_values
189 190 ) unless group_values.empty?
@@ -404,10 +405,10 class IssueQuery < Query
404 405
405 406 def sql_for_member_of_group_field(field, operator, value)
406 407 if operator == '*' # Any group
407 groups = Group.all
408 groups = Group.givable
408 409 operator = '=' # Override the operator since we want to find by assigned_to
409 410 elsif operator == "!*"
410 groups = Group.all
411 groups = Group.givable
411 412 operator = '!' # Override the operator since we want to find by assigned_to
412 413 else
413 414 groups = Group.where(:id => value).all
@@ -114,3 +114,6 class Principal < ActiveRecord::Base
114 114 true
115 115 end
116 116 end
117
118 require_dependency "user"
119 require_dependency "group"
@@ -32,7 +32,7 class Project < ActiveRecord::Base
32 32 has_many :memberships, :class_name => 'Member'
33 33 has_many :member_principals, :class_name => 'Member',
34 34 :include => :principal,
35 :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE})"
35 :conditions => "#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
36 36
37 37 has_many :enabled_modules, :dependent => :delete_all
38 38 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
@@ -191,11 +191,9 class Project < ActiveRecord::Base
191 191 statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
192 192 end
193 193 end
194 if user.logged?
195 user.projects_by_role.each do |role, projects|
196 if role.allowed_to?(permission) && projects.any?
197 statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
198 end
194 user.projects_by_role.each do |role, projects|
195 if role.allowed_to?(permission) && projects.any?
196 statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
199 197 end
200 198 end
201 199 if statement_by_role.empty?
@@ -213,6 +211,12 class Project < ActiveRecord::Base
213 211 end
214 212 end
215 213
214 def override_roles(role)
215 @override_members ||= memberships.where(:user_id => [GroupAnonymous.instance_id, GroupNonMember.instance_id]).all
216 member = @override_members.detect {|m| role.anonymous? ^ (m.user_id == GroupNonMember.instance_id)}
217 member ? member.roles : [role]
218 end
219
216 220 def principals
217 221 @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq
218 222 end
@@ -305,6 +309,7 class Project < ActiveRecord::Base
305 309 @actions_allowed = nil
306 310 @start_date = nil
307 311 @due_date = nil
312 @override_members = nil
308 313 base_reload(*args)
309 314 end
310 315
@@ -498,8 +503,13 class Project < ActiveRecord::Base
498 503
499 504 # Users/groups issues can be assigned to
500 505 def assignable_users
501 assignable = Setting.issue_group_assignment? ? member_principals : members
502 assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort
506 types = ['User']
507 types << 'Group' if Setting.issue_group_assignment?
508
509 member_principals.
510 select {|m| types.include?(m.principal.type) && m.roles.detect(&:assignable?)}.
511 map(&:principal).
512 sort
503 513 end
504 514
505 515 # Returns the mail addresses of users that should be always notified on project events
@@ -474,15 +474,15 class User < Principal
474 474
475 475 # Return user's roles for project
476 476 def roles_for_project(project)
477 roles = []
478 477 # No role on archived projects
479 return roles if project.nil? || project.archived?
478 return [] if project.nil? || project.archived?
480 479 if membership = membership(project)
481 roles = membership.roles
480 membership.roles.dup
481 elsif project.is_public?
482 project.override_roles(builtin_role)
482 483 else
483 roles << builtin_role
484 []
484 485 end
485 roles
486 486 end
487 487
488 488 # Return true if the user is a member of project
@@ -494,20 +494,28 class User < Principal
494 494 def projects_by_role
495 495 return @projects_by_role if @projects_by_role
496 496
497 @projects_by_role = Hash.new([])
498 memberships.each do |membership|
499 if membership.project
500 membership.roles.each do |role|
501 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
502 @projects_by_role[role] << membership.project
497 hash = Hash.new([])
498
499 members = Member.joins(:project).
500 where("#{Project.table_name}.status <> 9").
501 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, Group.builtin_id(self)).
502 preload(:project, :roles)
503
504 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
505 members.each do |member|
506 if member.project
507 member.roles.each do |role|
508 hash[role] = [] unless hash.key?(role)
509 hash[role] << member.project
503 510 end
504 511 end
505 512 end
506 @projects_by_role.each do |role, projects|
513
514 hash.each do |role, projects|
507 515 projects.uniq!
508 516 end
509 517
510 @projects_by_role
518 @projects_by_role = hash
511 519 end
512 520
513 521 # Returns true if user is arg or belongs to arg
@@ -1,7 +1,9
1 1 <%= error_messages_for @group %>
2 2
3 3 <div class="box tabular">
4 <p><%= f.text_field :name, :required => true, :size => 60 %></p>
4 <p><%= f.text_field :name, :required => true, :size => 60,
5 :disabled => !@group.safe_attribute?('name') %></p>
6
5 7 <% @group.custom_field_values.each do |value| %>
6 8 <p><%= custom_field_tag_with_label :group, value %></p>
7 9 <% end %>
@@ -1,4 +1,4
1 <%= labelled_form_for @group do |f| %>
1 <%= labelled_form_for @group, :url => group_path(@group) do |f| %>
2 2 <%= render :partial => 'form', :locals => { :f => f } %>
3 3 <%= submit_tag l(:button_save) %>
4 4 <% end %>
@@ -1,3 +1,3
1 1 <%= title [l(:label_group_plural), groups_path], @group.name %>
2 2
3 <%= render_tabs group_settings_tabs %>
3 <%= render_tabs group_settings_tabs(@group) %>
@@ -3,6 +3,7 api.array :groups do
3 3 api.group do
4 4 api.id group.id
5 5 api.name group.lastname
6 api.builtin group.builtin_type if group.builtin_type
6 7
7 8 render_api_custom_values group.visible_custom_field_values, api
8 9 end
@@ -12,10 +12,10
12 12 </tr></thead>
13 13 <tbody>
14 14 <% @groups.each do |group| %>
15 <tr id="group-<%= group.id %>" class="<%= cycle 'odd', 'even' %>">
15 <tr id="group-<%= group.id %>" class="<%= cycle 'odd', 'even' %> <%= "builtin" if group.builtin? %>">
16 16 <td class="name"><%= link_to h(group), edit_group_path(group) %></td>
17 <td class="user_count"><%= @user_count_by_group_id[group.id] || 0 %></td>
18 <td class="buttons"><%= delete_link group %></td>
17 <td class="user_count"><%= (@user_count_by_group_id[group.id] || 0) unless group.builtin? %></td>
18 <td class="buttons"><%= delete_link group unless group.builtin? %></td>
19 19 </tr>
20 20 <% end %>
21 21 </tbody>
@@ -1,6 +1,7
1 1 api.group do
2 2 api.id @group.id
3 3 api.name @group.lastname
4 api.builtin @group.builtin_type if @group.builtin_type
4 5
5 6 render_api_custom_values @group.visible_custom_field_values, api
6 7
@@ -8,7 +9,7 api.group do
8 9 @group.users.each do |user|
9 10 api.user :id => user.id, :name => user.name
10 11 end
11 end if include_in_api_response?('users')
12 end if include_in_api_response?('users') && !@group.builtin?
12 13
13 14 api.array :memberships do
14 15 @group.memberships.each do |membership|
@@ -1,6 +1,6
1 1 <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %>
2 2 <div class="box">
3 <% Group.all.sort.each do |group| %>
3 <% Group.givable.sort.each do |group| %>
4 4 <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group), :id => nil %> <%=h group %></label><br />
5 5 <% end %>
6 6 <%= hidden_field_tag 'user[group_ids][]', '' %>
@@ -851,6 +851,8 en:
851 851 label_group: Group
852 852 label_group_plural: Groups
853 853 label_group_new: New group
854 label_group_anonymous: Anonymous users
855 label_group_non_member: Non member users
854 856 label_time_entry_plural: Spent time
855 857 label_version_sharing_none: Not shared
856 858 label_version_sharing_descendants: With subprojects
@@ -871,6 +871,8 fr:
871 871 label_group: Groupe
872 872 label_group_plural: Groupes
873 873 label_group_new: Nouveau groupe
874 label_group_anonymous: Utilisateurs anonymes
875 label_group_non_member: Utilisateurs non membres
874 876 label_time_entry_plural: Temps passΓ©
875 877 label_version_sharing_none: Non partagΓ©
876 878 label_version_sharing_descendants: Avec les sous-projets
@@ -248,7 +248,12 sub RedmineDSN {
248 248 AND (
249 249 roles.id IN (SELECT member_roles.role_id FROM members, member_roles WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id)
250 250 OR
251 (roles.builtin=1 AND cast(projects.is_public as CHAR) IN ('t', '1'))
251 (cast(projects.is_public as CHAR) IN ('t', '1')
252 AND (roles.builtin=1
253 OR roles.id IN (SELECT member_roles.role_id FROM members, member_roles, users g
254 WHERE members.user_id = g.id AND members.project_id = projects.id AND members.id = member_roles.member_id
255 AND g.type = 'GroupNonMember'))
256 )
252 257 )
253 258 AND roles.permissions IS NOT NULL";
254 259 $self->{RedmineQuery} = trim($query);
@@ -328,7 +333,7 sub access_handler {
328 333 my $project_id = get_project_identifier($r);
329 334
330 335 $r->set_handlers(PerlAuthenHandler => [\&OK])
331 if is_public_project($project_id, $r) && anonymous_role_allows_browse_repository($r);
336 if is_public_project($project_id, $r) && anonymous_allowed_to_browse_repository($project_id, $r);
332 337
333 338 return OK
334 339 }
@@ -400,15 +405,20 sub is_public_project {
400 405 $ret;
401 406 }
402 407
403 sub anonymous_role_allows_browse_repository {
408 sub anonymous_allowed_to_browse_repository {
409 my $project_id = shift;
404 410 my $r = shift;
405 411
406 412 my $dbh = connect_database($r);
407 413 my $sth = $dbh->prepare(
408 "SELECT permissions FROM roles WHERE builtin = 2;"
414 "SELECT permissions FROM roles WHERE permissions like '%browse_repository%'
415 AND (roles.builtin = 2
416 OR roles.id IN (SELECT member_roles.role_id FROM projects, members, member_roles, users
417 WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id
418 AND projects.identifier = ? AND users.type = 'GroupAnonymous'));"
409 419 );
410 420
411 $sth->execute();
421 $sth->execute($project_id);
412 422 my $ret = 0;
413 423 if (my @row = $sth->fetchrow_array) {
414 424 if ($row[0] =~ /:browse_repository/) {
@@ -241,6 +241,8 table p {margin:0;}
241 241 .odd {background-color:#f6f7f8;}
242 242 .even {background-color: #fff;}
243 243
244 tr.builtin td.name {font-style:italic;}
245
244 246 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
245 247 a.sort.asc { background-image: url(../images/sort_asc.png); }
246 248 a.sort.desc { background-image: url(../images/sort_desc.png); }
@@ -609,7 +611,8 select.bool_cf {width:auto !important;}
609 611 #users_for_watcher {height: 200px; overflow:auto;}
610 612 #users_for_watcher label {display: block;}
611 613
612 table.members td.group { padding-left: 20px; background: url(../images/group.png) no-repeat 0% 50%; }
614 table.members td.name {padding-left: 20px;}
615 table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;}
613 616
614 617 input#principal_search, input#user_search {width:90%}
615 618
@@ -27,6 +27,12 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase
27 27 assert_success "ls", svn_url
28 28 end
29 29
30 def test_anonymous_read_on_public_repo_with_anonymous_group_permission_should_succeed
31 Role.anonymous.remove_permission! :browse_repository
32 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
33 assert_success "ls", svn_url
34 end
35
30 36 def test_anonymous_read_on_public_repo_without_permission_should_fail
31 37 Role.anonymous.remove_permission! :browse_repository
32 38 assert_failure "ls", svn_url
@@ -55,6 +61,15 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase
55 61 end
56 62 end
57 63
64 def test_non_member_read_on_public_repo_with_non_member_group_permission_should_succeed
65 Role.anonymous.remove_permission! :browse_repository
66 Role.non_member.remove_permission! :browse_repository
67 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
68 with_credentials "miscuser8", "foo" do
69 assert_success "ls", svn_url
70 end
71 end
72
58 73 def test_non_member_read_on_public_repo_without_permission_should_fail
59 74 Role.anonymous.remove_permission! :browse_repository
60 75 Role.non_member.remove_permission! :browse_repository
@@ -65,6 +65,7 module RedminePmTest
65 65 @command = args.join(' ')
66 66 @status = nil
67 67 IO.popen("#{command} 2>&1") do |io|
68 io.set_encoding("ASCII-8BIT") if io.respond_to?(:set_encoding)
68 69 @response = io.read
69 70 end
70 71 @status = $?.exitstatus
@@ -161,9 +161,20 groups_010:
161 161 id: 10
162 162 lastname: A Team
163 163 type: Group
164 status: 1
164 165 groups_011:
165 166 id: 11
166 167 lastname: B Team
167 168 type: Group
169 status: 1
170 groups_non_member:
171 id: 12
172 lastname: Non member users
173 type: GroupNonMember
174 status: 1
175 groups_anonymous:
176 id: 13
177 lastname: Anonymous users
178 type: GroupAnonymous
179 status: 1
168 180
169
@@ -29,12 +29,13 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
29 29 assert_response 401
30 30 end
31 31
32 test "GET /groups.xml should return groups" do
32 test "GET /groups.xml should return givable groups" do
33 33 get '/groups.xml', {}, credentials('admin')
34 34 assert_response :success
35 35 assert_equal 'application/xml', response.content_type
36 36
37 37 assert_select 'groups' do
38 assert_select 'group', Group.givable.count
38 39 assert_select 'group' do
39 40 assert_select 'name', :text => 'A Team'
40 41 assert_select 'id', :text => '10'
@@ -42,6 +43,24 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
42 43 end
43 44 end
44 45
46 test "GET /groups.xml?builtin=1 should return all groups" do
47 get '/groups.xml?builtin=1', {}, credentials('admin')
48 assert_response :success
49 assert_equal 'application/xml', response.content_type
50
51 assert_select 'groups' do
52 assert_select 'group', Group.givable.count + 2
53 assert_select 'group' do
54 assert_select 'builtin', :text => 'non_member'
55 assert_select 'id', :text => '12'
56 end
57 assert_select 'group' do
58 assert_select 'builtin', :text => 'anonymous'
59 assert_select 'id', :text => '13'
60 end
61 end
62 end
63
45 64 test "GET /groups.json should require authentication" do
46 65 get '/groups.json'
47 66 assert_response 401
@@ -60,7 +79,7 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
60 79 assert_equal({'id' => 10, 'name' => 'A Team'}, group)
61 80 end
62 81
63 test "GET /groups/:id.xml should return the group with its users" do
82 test "GET /groups/:id.xml should return the group" do
64 83 get '/groups/10.xml', {}, credentials('admin')
65 84 assert_response :success
66 85 assert_equal 'application/xml', response.content_type
@@ -71,6 +90,17 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
71 90 end
72 91 end
73 92
93 test "GET /groups/:id.xml should return the builtin group" do
94 get '/groups/12.xml', {}, credentials('admin')
95 assert_response :success
96 assert_equal 'application/xml', response.content_type
97
98 assert_select 'group' do
99 assert_select 'builtin', :text => 'non_member'
100 assert_select 'id', :text => '12'
101 end
102 end
103
74 104 test "GET /groups/:id.xml should include users if requested" do
75 105 get '/groups/10.xml?include=users', {}, credentials('admin')
76 106 assert_response :success
@@ -65,6 +65,27 class IssuesTest < ActionController::IntegrationTest
65 65 assert_equal 1, issue.status.id
66 66 end
67 67
68 def test_create_issue_by_anonymous_without_permission_should_fail
69 Role.anonymous.remove_permission! :add_issues
70
71 assert_no_difference 'Issue.count' do
72 post 'projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"}
73 end
74 assert_response 302
75 end
76
77 def test_create_issue_by_anonymous_with_custom_permission_should_succeed
78 Role.anonymous.remove_permission! :add_issues
79 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [3])
80
81 assert_difference 'Issue.count' do
82 post 'projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"}
83 end
84 assert_response 302
85 issue = Issue.order("id DESC").first
86 assert_equal User.anonymous, issue.author
87 end
88
68 89 # add then remove 2 attachments to an issue
69 90 def test_issue_attachments
70 91 log_user('jsmith', 'jsmith')
@@ -133,4 +133,20 class GroupTest < ActiveSupport::TestCase
133 133
134 134 assert_equal nil, Issue.find(1).assigned_to_id
135 135 end
136
137 def test_builtin_id_with_anonymous_user_should_return_anonymous_group
138 assert_equal 13, Group.builtin_id(User.anonymous)
139 end
140
141 def test_builtin_id_with_anonymous_role_should_return_anonymous_group
142 assert_equal 13, Group.builtin_id(Role.anonymous)
143 end
144
145 def test_builtin_id_with_user_should_return_non_member_group
146 assert_equal 12, Group.builtin_id(User.find(1))
147 end
148
149 def test_builtin_id_with_non_member_role_should_return_non_member_group
150 assert_equal 12, Group.builtin_id(Role.non_member)
151 end
136 152 end
@@ -219,6 +219,16 class IssueTest < ActiveSupport::TestCase
219 219 assert_visibility_match User.anonymous, issues
220 220 end
221 221
222 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
223 Role.anonymous.remove_permission!(:view_issues)
224 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
225
226 issues = Issue.visible(User.anonymous).all
227 assert issues.any?
228 assert_equal [1], issues.map(&:project_id).uniq.sort
229 assert_visibility_match User.anonymous, issues
230 end
231
222 232 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
223 233 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
224 234 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
@@ -265,6 +275,17 class IssueTest < ActiveSupport::TestCase
265 275 assert_visibility_match user, issues
266 276 end
267 277
278 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
279 Role.non_member.remove_permission!(:view_issues)
280 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
281 user = User.find(9)
282
283 issues = Issue.visible(user).all
284 assert issues.any?
285 assert_equal [1], issues.map(&:project_id).uniq.sort
286 assert_visibility_match user, issues
287 end
288
268 289 def test_visible_scope_for_member
269 290 user = User.find(9)
270 291 # User should see issues of projects for which user has view_issues permissions only
@@ -1724,6 +1745,16 class IssueTest < ActiveSupport::TestCase
1724 1745 end
1725 1746 end
1726 1747
1748 def test_assignable_users_should_not_include_builtin_groups
1749 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
1750 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
1751 issue = Issue.new(:project => Project.find(1))
1752
1753 with_settings :issue_group_assignment => '1' do
1754 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
1755 end
1756 end
1757
1727 1758 def test_create_should_send_email_notification
1728 1759 ActionMailer::Base.deliveries.clear
1729 1760 issue = Issue.new(:project_id => 1, :tracker_id => 1,
@@ -55,17 +55,11 class PrincipalTest < ActiveSupport::TestCase
55 55 end
56 56
57 57 def test_sorted_scope_should_sort_users_before_groups
58 scope = Principal.where("type <> ?", 'AnonymousUser')
59 expected_order = scope.all.sort do |a, b|
60 if a.is_a?(User) && b.is_a?(Group)
61 -1
62 elsif a.is_a?(Group) && b.is_a?(User)
63 1
64 else
65 a.name.downcase <=> b.name.downcase
66 end
67 end
68 assert_equal expected_order.map(&:name).map(&:downcase),
58 scope = Principal.where(:type => ['User', 'Group'])
59 users = scope.select {|p| p.is_a?(User)}.sort
60 groups = scope.select {|p| p.is_a?(Group)}.sort
61
62 assert_equal (users + groups).map(&:name).map(&:downcase),
69 63 scope.sorted.map(&:name).map(&:downcase)
70 64 end
71 65
@@ -1240,7 +1240,7 class QueryTest < ActiveSupport::TestCase
1240 1240 assert query.available_filters.keys.include?("member_of_group")
1241 1241 assert_equal :list_optional, query.available_filters["member_of_group"][:type]
1242 1242 assert query.available_filters["member_of_group"][:values].present?
1243 assert_equal Group.all.sort.map {|g| [g.name, g.id.to_s]},
1243 assert_equal Group.givable.sort.map {|g| [g.name, g.id.to_s]},
1244 1244 query.available_filters["member_of_group"][:values].sort
1245 1245 end
1246 1246
@@ -838,14 +838,42 class UserTest < ActiveSupport::TestCase
838 838 assert_nil membership
839 839 end
840 840
841 def test_roles_for_project
842 # user with a role
841 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
843 842 roles = @jsmith.roles_for_project(Project.find(1))
844 843 assert_kind_of Role, roles.first
845 assert_equal "Manager", roles.first.name
844 assert_equal ["Manager"], roles.map(&:name)
845 end
846
847 def test_roles_for_project_with_member_on_private_project_should_return_roles
848 Project.find(1).update_attribute :is_public, false
849
850 roles = @jsmith.roles_for_project(Project.find(1))
851 assert_kind_of Role, roles.first
852 assert_equal ["Manager"], roles.map(&:name)
853 end
854
855 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
856 roles = User.find(8).roles_for_project(Project.find(1))
857 assert_equal ["Non member"], roles.map(&:name)
858 end
859
860 def test_roles_for_project_with_non_member_with_public_project_should_return_no_roles
861 Project.find(1).update_attribute :is_public, false
862
863 roles = User.find(8).roles_for_project(Project.find(1))
864 assert_equal [], roles.map(&:name)
865 end
866
867 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
868 roles = User.anonymous.roles_for_project(Project.find(1))
869 assert_equal ["Anonymous"], roles.map(&:name)
870 end
846 871
847 # user with no role
848 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
872 def test_roles_for_project_with_anonymous_with_public_project_should_return_no_roles
873 Project.find(1).update_attribute :is_public, false
874
875 roles = User.anonymous.roles_for_project(Project.find(1))
876 assert_equal [], roles.map(&:name)
849 877 end
850 878
851 879 def test_projects_by_role_for_user_with_role
General Comments 0
You need to be logged in to leave comments. Login now