##// 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 helper :custom_fields
25 helper :custom_fields
26
26
27 def index
27 def index
28 @groups = Group.sorted.all
29 respond_to do |format|
28 respond_to do |format|
30 format.html {
29 format.html {
30 @groups = Group.sorted.all
31 @user_count_by_group_id = user_count_by_group_id
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 end
38 end
35 end
39 end
36
40
@@ -18,11 +18,12
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module GroupsHelper
20 module GroupsHelper
21 def group_settings_tabs
21 def group_settings_tabs(group)
22 tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
22 tabs = []
23 {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
23 tabs << {:name => 'general', :partial => 'groups/general', :label => :label_general}
24 {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
24 tabs << {:name => 'users', :partial => 'groups/users', :label => :label_user_plural} if group.givable?
25 ]
25 tabs << {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
26 tabs
26 end
27 end
27
28
28 def render_principals_for_new_group_users(group)
29 def render_principals_for_new_group_users(group)
@@ -46,7 +46,7 module UsersHelper
46 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
46 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
47 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
47 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
48 ]
48 ]
49 if Group.all.any?
49 if Group.givable.any?
50 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
50 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
51 end
51 end
52 tabs
52 tabs
@@ -31,17 +31,18 class Group < Principal
31
31
32 before_destroy :remove_references_before_destroy
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 scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)}
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 safe_attributes 'name',
38 safe_attributes 'name',
38 'user_ids',
39 'user_ids',
39 'custom_field_values',
40 'custom_field_values',
40 'custom_fields',
41 'custom_fields',
41 :if => lambda {|group, user| user.admin?}
42 :if => lambda {|group, user| user.admin? && !group.builtin?}
42
43
43 def to_s
44 def to_s
44 lastname.to_s
45 name.to_s
45 end
46 end
46
47
47 def name
48 def name
@@ -52,6 +53,20 class Group < Principal
52 self.lastname = arg
53 self.lastname = arg
53 end
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 def user_added(user)
70 def user_added(user)
56 members.each do |member|
71 members.each do |member|
57 next if member.project.nil?
72 next if member.project.nil?
@@ -80,6 +95,18 class Group < Principal
80 super(attr_name, *args)
95 super(attr_name, *args)
81 end
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 private
110 private
84
111
85 # Removes references that are not handled by associations
112 # Removes references that are not handled by associations
@@ -89,3 +116,5 class Group < Principal
89 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
116 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
90 end
117 end
91 end
118 end
119
120 require_dependency "group_builtin"
@@ -147,6 +147,7 class IssueQuery < Query
147 end
147 end
148 principals.uniq!
148 principals.uniq!
149 principals.sort!
149 principals.sort!
150 principals.reject! {|p| p.is_a?(GroupBuiltin)}
150 users = principals.select {|p| p.is_a?(User)}
151 users = principals.select {|p| p.is_a?(User)}
151
152
152 add_available_filter "status_id",
153 add_available_filter "status_id",
@@ -183,7 +184,7 class IssueQuery < Query
183 :type => :list_optional, :values => assigned_to_values
184 :type => :list_optional, :values => assigned_to_values
184 ) unless assigned_to_values.empty?
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 add_available_filter("member_of_group",
188 add_available_filter("member_of_group",
188 :type => :list_optional, :values => group_values
189 :type => :list_optional, :values => group_values
189 ) unless group_values.empty?
190 ) unless group_values.empty?
@@ -404,10 +405,10 class IssueQuery < Query
404
405
405 def sql_for_member_of_group_field(field, operator, value)
406 def sql_for_member_of_group_field(field, operator, value)
406 if operator == '*' # Any group
407 if operator == '*' # Any group
407 groups = Group.all
408 groups = Group.givable
408 operator = '=' # Override the operator since we want to find by assigned_to
409 operator = '=' # Override the operator since we want to find by assigned_to
409 elsif operator == "!*"
410 elsif operator == "!*"
410 groups = Group.all
411 groups = Group.givable
411 operator = '!' # Override the operator since we want to find by assigned_to
412 operator = '!' # Override the operator since we want to find by assigned_to
412 else
413 else
413 groups = Group.where(:id => value).all
414 groups = Group.where(:id => value).all
@@ -114,3 +114,6 class Principal < ActiveRecord::Base
114 true
114 true
115 end
115 end
116 end
116 end
117
118 require_dependency "user"
119 require_dependency "group"
@@ -32,7 +32,7 class Project < ActiveRecord::Base
32 has_many :memberships, :class_name => 'Member'
32 has_many :memberships, :class_name => 'Member'
33 has_many :member_principals, :class_name => 'Member',
33 has_many :member_principals, :class_name => 'Member',
34 :include => :principal,
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 has_many :enabled_modules, :dependent => :delete_all
37 has_many :enabled_modules, :dependent => :delete_all
38 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
38 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
@@ -191,11 +191,9 class Project < ActiveRecord::Base
191 statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
191 statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
192 end
192 end
193 end
193 end
194 if user.logged?
194 user.projects_by_role.each do |role, projects|
195 user.projects_by_role.each do |role, projects|
195 if role.allowed_to?(permission) && projects.any?
196 if role.allowed_to?(permission) && projects.any?
196 statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
197 statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
198 end
199 end
197 end
200 end
198 end
201 if statement_by_role.empty?
199 if statement_by_role.empty?
@@ -213,6 +211,12 class Project < ActiveRecord::Base
213 end
211 end
214 end
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 def principals
220 def principals
217 @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq
221 @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq
218 end
222 end
@@ -305,6 +309,7 class Project < ActiveRecord::Base
305 @actions_allowed = nil
309 @actions_allowed = nil
306 @start_date = nil
310 @start_date = nil
307 @due_date = nil
311 @due_date = nil
312 @override_members = nil
308 base_reload(*args)
313 base_reload(*args)
309 end
314 end
310
315
@@ -498,8 +503,13 class Project < ActiveRecord::Base
498
503
499 # Users/groups issues can be assigned to
504 # Users/groups issues can be assigned to
500 def assignable_users
505 def assignable_users
501 assignable = Setting.issue_group_assignment? ? member_principals : members
506 types = ['User']
502 assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort
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 end
513 end
504
514
505 # Returns the mail addresses of users that should be always notified on project events
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 # Return user's roles for project
475 # Return user's roles for project
476 def roles_for_project(project)
476 def roles_for_project(project)
477 roles = []
478 # No role on archived projects
477 # No role on archived projects
479 return roles if project.nil? || project.archived?
478 return [] if project.nil? || project.archived?
480 if membership = membership(project)
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 else
483 else
483 roles << builtin_role
484 []
484 end
485 end
485 roles
486 end
486 end
487
487
488 # Return true if the user is a member of project
488 # Return true if the user is a member of project
@@ -494,20 +494,28 class User < Principal
494 def projects_by_role
494 def projects_by_role
495 return @projects_by_role if @projects_by_role
495 return @projects_by_role if @projects_by_role
496
496
497 @projects_by_role = Hash.new([])
497 hash = Hash.new([])
498 memberships.each do |membership|
498
499 if membership.project
499 members = Member.joins(:project).
500 membership.roles.each do |role|
500 where("#{Project.table_name}.status <> 9").
501 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
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 @projects_by_role[role] << membership.project
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 end
510 end
504 end
511 end
505 end
512 end
506 @projects_by_role.each do |role, projects|
513
514 hash.each do |role, projects|
507 projects.uniq!
515 projects.uniq!
508 end
516 end
509
517
510 @projects_by_role
518 @projects_by_role = hash
511 end
519 end
512
520
513 # Returns true if user is arg or belongs to arg
521 # Returns true if user is arg or belongs to arg
@@ -1,7 +1,9
1 <%= error_messages_for @group %>
1 <%= error_messages_for @group %>
2
2
3 <div class="box tabular">
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 <% @group.custom_field_values.each do |value| %>
7 <% @group.custom_field_values.each do |value| %>
6 <p><%= custom_field_tag_with_label :group, value %></p>
8 <p><%= custom_field_tag_with_label :group, value %></p>
7 <% end %>
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 <%= render :partial => 'form', :locals => { :f => f } %>
2 <%= render :partial => 'form', :locals => { :f => f } %>
3 <%= submit_tag l(:button_save) %>
3 <%= submit_tag l(:button_save) %>
4 <% end %>
4 <% end %>
@@ -1,3 +1,3
1 <%= title [l(:label_group_plural), groups_path], @group.name %>
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 api.group do
3 api.group do
4 api.id group.id
4 api.id group.id
5 api.name group.lastname
5 api.name group.lastname
6 api.builtin group.builtin_type if group.builtin_type
6
7
7 render_api_custom_values group.visible_custom_field_values, api
8 render_api_custom_values group.visible_custom_field_values, api
8 end
9 end
@@ -12,10 +12,10
12 </tr></thead>
12 </tr></thead>
13 <tbody>
13 <tbody>
14 <% @groups.each do |group| %>
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 <td class="name"><%= link_to h(group), edit_group_path(group) %></td>
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>
17 <td class="user_count"><%= (@user_count_by_group_id[group.id] || 0) unless group.builtin? %></td>
18 <td class="buttons"><%= delete_link group %></td>
18 <td class="buttons"><%= delete_link group unless group.builtin? %></td>
19 </tr>
19 </tr>
20 <% end %>
20 <% end %>
21 </tbody>
21 </tbody>
@@ -1,6 +1,7
1 api.group do
1 api.group do
2 api.id @group.id
2 api.id @group.id
3 api.name @group.lastname
3 api.name @group.lastname
4 api.builtin @group.builtin_type if @group.builtin_type
4
5
5 render_api_custom_values @group.visible_custom_field_values, api
6 render_api_custom_values @group.visible_custom_field_values, api
6
7
@@ -8,7 +9,7 api.group do
8 @group.users.each do |user|
9 @group.users.each do |user|
9 api.user :id => user.id, :name => user.name
10 api.user :id => user.id, :name => user.name
10 end
11 end
11 end if include_in_api_response?('users')
12 end if include_in_api_response?('users') && !@group.builtin?
12
13
13 api.array :memberships do
14 api.array :memberships do
14 @group.memberships.each do |membership|
15 @group.memberships.each do |membership|
@@ -1,6 +1,6
1 <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %>
1 <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %>
2 <div class="box">
2 <div class="box">
3 <% Group.all.sort.each do |group| %>
3 <% Group.givable.sort.each do |group| %>
4 <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group), :id => nil %> <%=h group %></label><br />
4 <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group), :id => nil %> <%=h group %></label><br />
5 <% end %>
5 <% end %>
6 <%= hidden_field_tag 'user[group_ids][]', '' %>
6 <%= hidden_field_tag 'user[group_ids][]', '' %>
@@ -851,6 +851,8 en:
851 label_group: Group
851 label_group: Group
852 label_group_plural: Groups
852 label_group_plural: Groups
853 label_group_new: New group
853 label_group_new: New group
854 label_group_anonymous: Anonymous users
855 label_group_non_member: Non member users
854 label_time_entry_plural: Spent time
856 label_time_entry_plural: Spent time
855 label_version_sharing_none: Not shared
857 label_version_sharing_none: Not shared
856 label_version_sharing_descendants: With subprojects
858 label_version_sharing_descendants: With subprojects
@@ -871,6 +871,8 fr:
871 label_group: Groupe
871 label_group: Groupe
872 label_group_plural: Groupes
872 label_group_plural: Groupes
873 label_group_new: Nouveau groupe
873 label_group_new: Nouveau groupe
874 label_group_anonymous: Utilisateurs anonymes
875 label_group_non_member: Utilisateurs non membres
874 label_time_entry_plural: Temps passΓ©
876 label_time_entry_plural: Temps passΓ©
875 label_version_sharing_none: Non partagΓ©
877 label_version_sharing_none: Non partagΓ©
876 label_version_sharing_descendants: Avec les sous-projets
878 label_version_sharing_descendants: Avec les sous-projets
@@ -248,7 +248,12 sub RedmineDSN {
248 AND (
248 AND (
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)
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 OR
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 AND roles.permissions IS NOT NULL";
258 AND roles.permissions IS NOT NULL";
254 $self->{RedmineQuery} = trim($query);
259 $self->{RedmineQuery} = trim($query);
@@ -328,7 +333,7 sub access_handler {
328 my $project_id = get_project_identifier($r);
333 my $project_id = get_project_identifier($r);
329
334
330 $r->set_handlers(PerlAuthenHandler => [\&OK])
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 return OK
338 return OK
334 }
339 }
@@ -400,15 +405,20 sub is_public_project {
400 $ret;
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 my $r = shift;
410 my $r = shift;
405
411
406 my $dbh = connect_database($r);
412 my $dbh = connect_database($r);
407 my $sth = $dbh->prepare(
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 my $ret = 0;
422 my $ret = 0;
413 if (my @row = $sth->fetchrow_array) {
423 if (my @row = $sth->fetchrow_array) {
414 if ($row[0] =~ /:browse_repository/) {
424 if ($row[0] =~ /:browse_repository/) {
@@ -241,6 +241,8 table p {margin:0;}
241 .odd {background-color:#f6f7f8;}
241 .odd {background-color:#f6f7f8;}
242 .even {background-color: #fff;}
242 .even {background-color: #fff;}
243
243
244 tr.builtin td.name {font-style:italic;}
245
244 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
246 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
245 a.sort.asc { background-image: url(../images/sort_asc.png); }
247 a.sort.asc { background-image: url(../images/sort_asc.png); }
246 a.sort.desc { background-image: url(../images/sort_desc.png); }
248 a.sort.desc { background-image: url(../images/sort_desc.png); }
@@ -609,7 +611,8 select.bool_cf {width:auto !important;}
609 #users_for_watcher {height: 200px; overflow:auto;}
611 #users_for_watcher {height: 200px; overflow:auto;}
610 #users_for_watcher label {display: block;}
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 input#principal_search, input#user_search {width:90%}
617 input#principal_search, input#user_search {width:90%}
615
618
@@ -27,6 +27,12 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase
27 assert_success "ls", svn_url
27 assert_success "ls", svn_url
28 end
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 def test_anonymous_read_on_public_repo_without_permission_should_fail
36 def test_anonymous_read_on_public_repo_without_permission_should_fail
31 Role.anonymous.remove_permission! :browse_repository
37 Role.anonymous.remove_permission! :browse_repository
32 assert_failure "ls", svn_url
38 assert_failure "ls", svn_url
@@ -55,6 +61,15 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase
55 end
61 end
56 end
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 def test_non_member_read_on_public_repo_without_permission_should_fail
73 def test_non_member_read_on_public_repo_without_permission_should_fail
59 Role.anonymous.remove_permission! :browse_repository
74 Role.anonymous.remove_permission! :browse_repository
60 Role.non_member.remove_permission! :browse_repository
75 Role.non_member.remove_permission! :browse_repository
@@ -65,6 +65,7 module RedminePmTest
65 @command = args.join(' ')
65 @command = args.join(' ')
66 @status = nil
66 @status = nil
67 IO.popen("#{command} 2>&1") do |io|
67 IO.popen("#{command} 2>&1") do |io|
68 io.set_encoding("ASCII-8BIT") if io.respond_to?(:set_encoding)
68 @response = io.read
69 @response = io.read
69 end
70 end
70 @status = $?.exitstatus
71 @status = $?.exitstatus
@@ -161,9 +161,20 groups_010:
161 id: 10
161 id: 10
162 lastname: A Team
162 lastname: A Team
163 type: Group
163 type: Group
164 status: 1
164 groups_011:
165 groups_011:
165 id: 11
166 id: 11
166 lastname: B Team
167 lastname: B Team
167 type: Group
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 assert_response 401
29 assert_response 401
30 end
30 end
31
31
32 test "GET /groups.xml should return groups" do
32 test "GET /groups.xml should return givable groups" do
33 get '/groups.xml', {}, credentials('admin')
33 get '/groups.xml', {}, credentials('admin')
34 assert_response :success
34 assert_response :success
35 assert_equal 'application/xml', response.content_type
35 assert_equal 'application/xml', response.content_type
36
36
37 assert_select 'groups' do
37 assert_select 'groups' do
38 assert_select 'group', Group.givable.count
38 assert_select 'group' do
39 assert_select 'group' do
39 assert_select 'name', :text => 'A Team'
40 assert_select 'name', :text => 'A Team'
40 assert_select 'id', :text => '10'
41 assert_select 'id', :text => '10'
@@ -42,6 +43,24 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
42 end
43 end
43 end
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 test "GET /groups.json should require authentication" do
64 test "GET /groups.json should require authentication" do
46 get '/groups.json'
65 get '/groups.json'
47 assert_response 401
66 assert_response 401
@@ -60,7 +79,7 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
60 assert_equal({'id' => 10, 'name' => 'A Team'}, group)
79 assert_equal({'id' => 10, 'name' => 'A Team'}, group)
61 end
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 get '/groups/10.xml', {}, credentials('admin')
83 get '/groups/10.xml', {}, credentials('admin')
65 assert_response :success
84 assert_response :success
66 assert_equal 'application/xml', response.content_type
85 assert_equal 'application/xml', response.content_type
@@ -71,6 +90,17 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
71 end
90 end
72 end
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 test "GET /groups/:id.xml should include users if requested" do
104 test "GET /groups/:id.xml should include users if requested" do
75 get '/groups/10.xml?include=users', {}, credentials('admin')
105 get '/groups/10.xml?include=users', {}, credentials('admin')
76 assert_response :success
106 assert_response :success
@@ -65,6 +65,27 class IssuesTest < ActionController::IntegrationTest
65 assert_equal 1, issue.status.id
65 assert_equal 1, issue.status.id
66 end
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 # add then remove 2 attachments to an issue
89 # add then remove 2 attachments to an issue
69 def test_issue_attachments
90 def test_issue_attachments
70 log_user('jsmith', 'jsmith')
91 log_user('jsmith', 'jsmith')
@@ -133,4 +133,20 class GroupTest < ActiveSupport::TestCase
133
133
134 assert_equal nil, Issue.find(1).assigned_to_id
134 assert_equal nil, Issue.find(1).assigned_to_id
135 end
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 end
152 end
@@ -219,6 +219,16 class IssueTest < ActiveSupport::TestCase
219 assert_visibility_match User.anonymous, issues
219 assert_visibility_match User.anonymous, issues
220 end
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 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
232 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
223 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
233 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
224 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
234 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
@@ -265,6 +275,17 class IssueTest < ActiveSupport::TestCase
265 assert_visibility_match user, issues
275 assert_visibility_match user, issues
266 end
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 def test_visible_scope_for_member
289 def test_visible_scope_for_member
269 user = User.find(9)
290 user = User.find(9)
270 # User should see issues of projects for which user has view_issues permissions only
291 # User should see issues of projects for which user has view_issues permissions only
@@ -1724,6 +1745,16 class IssueTest < ActiveSupport::TestCase
1724 end
1745 end
1725 end
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 def test_create_should_send_email_notification
1758 def test_create_should_send_email_notification
1728 ActionMailer::Base.deliveries.clear
1759 ActionMailer::Base.deliveries.clear
1729 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1760 issue = Issue.new(:project_id => 1, :tracker_id => 1,
@@ -55,17 +55,11 class PrincipalTest < ActiveSupport::TestCase
55 end
55 end
56
56
57 def test_sorted_scope_should_sort_users_before_groups
57 def test_sorted_scope_should_sort_users_before_groups
58 scope = Principal.where("type <> ?", 'AnonymousUser')
58 scope = Principal.where(:type => ['User', 'Group'])
59 expected_order = scope.all.sort do |a, b|
59 users = scope.select {|p| p.is_a?(User)}.sort
60 if a.is_a?(User) && b.is_a?(Group)
60 groups = scope.select {|p| p.is_a?(Group)}.sort
61 -1
61
62 elsif a.is_a?(Group) && b.is_a?(User)
62 assert_equal (users + groups).map(&:name).map(&:downcase),
63 1
64 else
65 a.name.downcase <=> b.name.downcase
66 end
67 end
68 assert_equal expected_order.map(&:name).map(&:downcase),
69 scope.sorted.map(&:name).map(&:downcase)
63 scope.sorted.map(&:name).map(&:downcase)
70 end
64 end
71
65
@@ -1240,7 +1240,7 class QueryTest < ActiveSupport::TestCase
1240 assert query.available_filters.keys.include?("member_of_group")
1240 assert query.available_filters.keys.include?("member_of_group")
1241 assert_equal :list_optional, query.available_filters["member_of_group"][:type]
1241 assert_equal :list_optional, query.available_filters["member_of_group"][:type]
1242 assert query.available_filters["member_of_group"][:values].present?
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 query.available_filters["member_of_group"][:values].sort
1244 query.available_filters["member_of_group"][:values].sort
1245 end
1245 end
1246
1246
@@ -838,14 +838,42 class UserTest < ActiveSupport::TestCase
838 assert_nil membership
838 assert_nil membership
839 end
839 end
840
840
841 def test_roles_for_project
841 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
842 # user with a role
843 roles = @jsmith.roles_for_project(Project.find(1))
842 roles = @jsmith.roles_for_project(Project.find(1))
844 assert_kind_of Role, roles.first
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
872 def test_roles_for_project_with_anonymous_with_public_project_should_return_no_roles
848 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
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 end
877 end
850
878
851 def test_projects_by_role_for_user_with_role
879 def test_projects_by_role_for_user_with_role
General Comments 0
You need to be logged in to leave comments. Login now