##// END OF EJS Templates
Add write control on repository from Redmine interface...
Nicolas Chuche -
r1789:f5f51f4f832d
parent child
Show More
@@ -0,0 +1,14
1 class AddRepositoryWriteAccess < ActiveRecord::Migration
2
3 def self.up
4 Role.find(:all).select { |r| not r.builtin? }.each do |r|
5 r.add_permission!(:commit_access)
6 end
7 end
8
9 def self.down
10 Role.find(:all).select { |r| not r.builtin? }.each do |r|
11 r.remove_permission!(:commit_access)
12 end
13 end
14 end
@@ -1,119 +1,137
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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 # Built-in roles
20 20 BUILTIN_NON_MEMBER = 1
21 21 BUILTIN_ANONYMOUS = 2
22 22
23 23 before_destroy :check_deletable
24 24 has_many :workflows, :dependent => :delete_all do
25 25 def copy(role)
26 26 raise "Can not copy workflow from a #{role.class}" unless role.is_a?(Role)
27 27 raise "Can not copy workflow from/to an unsaved role" if proxy_owner.new_record? || role.new_record?
28 28 clear
29 29 connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
30 30 " SELECT tracker_id, old_status_id, new_status_id, #{proxy_owner.id}" +
31 31 " FROM workflows" +
32 32 " WHERE role_id = #{role.id}"
33 33 end
34 34 end
35 35
36 36 has_many :members
37 37 acts_as_list
38 38
39 serialize :permissions
39 serialize :permissions, Array
40 40 attr_protected :builtin
41 41
42 42 validates_presence_of :name
43 43 validates_uniqueness_of :name
44 44 validates_length_of :name, :maximum => 30
45 45 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
46 46
47 47 def permissions
48 48 read_attribute(:permissions) || []
49 49 end
50 50
51 51 def permissions=(perms)
52 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact if perms
52 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
53 53 write_attribute(:permissions, perms)
54 54 end
55
56 def add_permission!(*perms)
57 self.permissions = [] unless permissions.is_a?(Array)
58
59 permissions_will_change!
60 perms.each do |p|
61 p = p.to_sym
62 permissions << p unless permissions.include?(p)
63 end
64 save!
65 end
66
67 def remove_permission!(*perms)
68 return unless permissions.is_a?(Array)
69 permissions_will_change!
70 perms.each { |p| permissions.delete(p.to_sym) }
71 save!
72 end
55 73
56 74 def <=>(role)
57 75 position <=> role.position
58 76 end
59 77
60 78 # Return true if the role is a builtin role
61 79 def builtin?
62 80 self.builtin != 0
63 81 end
64 82
65 83 # Return true if the role is a project member role
66 84 def member?
67 85 !self.builtin?
68 86 end
69 87
70 88 # Return true if role is allowed to do the specified action
71 89 # action can be:
72 90 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
73 91 # * a permission Symbol (eg. :edit_project)
74 92 def allowed_to?(action)
75 93 if action.is_a? Hash
76 94 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
77 95 else
78 96 allowed_permissions.include? action
79 97 end
80 98 end
81 99
82 100 # Return all the permissions that can be given to the role
83 101 def setable_permissions
84 102 setable_permissions = Redmine::AccessControl.permissions - Redmine::AccessControl.public_permissions
85 103 setable_permissions -= Redmine::AccessControl.members_only_permissions if self.builtin == BUILTIN_NON_MEMBER
86 104 setable_permissions -= Redmine::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
87 105 setable_permissions
88 106 end
89 107
90 108 # Find all the roles that can be given to a project member
91 109 def self.find_all_givable
92 110 find(:all, :conditions => {:builtin => 0}, :order => 'position')
93 111 end
94 112
95 113 # Return the builtin 'non member' role
96 114 def self.non_member
97 115 find(:first, :conditions => {:builtin => BUILTIN_NON_MEMBER}) || raise('Missing non-member builtin role.')
98 116 end
99 117
100 118 # Return the builtin 'anonymous' role
101 119 def self.anonymous
102 120 find(:first, :conditions => {:builtin => BUILTIN_ANONYMOUS}) || raise('Missing anonymous builtin role.')
103 121 end
104 122
105 123
106 124 private
107 125 def allowed_permissions
108 126 @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name}
109 127 end
110 128
111 129 def allowed_actions
112 130 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
113 131 end
114 132
115 133 def check_deletable
116 134 raise "Can't delete role" if members.any?
117 135 raise "Can't delete builtin role" if builtin?
118 136 end
119 137 end
@@ -1,340 +1,343
1 1 package Apache::Authn::Redmine;
2 2
3 3 =head1 Apache::Authn::Redmine
4 4
5 5 Redmine - a mod_perl module to authenticate webdav subversion users
6 6 against redmine database
7 7
8 8 =head1 SYNOPSIS
9 9
10 10 This module allow anonymous users to browse public project and
11 11 registred users to browse and commit their project. Authentication is
12 12 done against the redmine database or the LDAP configured in redmine.
13 13
14 14 This method is far simpler than the one with pam_* and works with all
15 15 database without an hassle but you need to have apache/mod_perl on the
16 16 svn server.
17 17
18 18 =head1 INSTALLATION
19 19
20 20 For this to automagically work, you need to have a recent reposman.rb
21 21 (after r860) and if you already use reposman, read the last section to
22 22 migrate.
23 23
24 24 Sorry ruby users but you need some perl modules, at least mod_perl2,
25 25 DBI and DBD::mysql (or the DBD driver for you database as it should
26 26 work on allmost all databases).
27 27
28 28 On debian/ubuntu you must do :
29 29
30 30 aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl
31 31
32 32 If your Redmine users use LDAP authentication, you will also need
33 33 Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
34 34
35 35 aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
36 36
37 37 =head1 CONFIGURATION
38 38
39 39 ## This module has to be in your perl path
40 40 ## eg: /usr/lib/perl5/Apache/Authn/Redmine.pm
41 41 PerlLoadModule Apache::Authn::Redmine
42 42 <Location /svn>
43 43 DAV svn
44 44 SVNParentPath "/var/svn"
45 45
46 46 AuthType Basic
47 47 AuthName redmine
48 48 Require valid-user
49 49
50 50 PerlAccessHandler Apache::Authn::Redmine::access_handler
51 51 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
52 52
53 53 ## for mysql
54 54 RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
55 55 ## for postgres
56 56 # RedmineDSN "DBI:Pg:dbname=databasename;host=my.db.server"
57 57
58 58 RedmineDbUser "redmine"
59 59 RedmineDbPass "password"
60 60 ## Optional where clause (fulltext search would be slow and
61 61 ## database dependant).
62 62 # RedmineDbWhereClause "and members.role_id IN (1,2)"
63 63 ## Optional credentials cache size
64 64 # RedmineCacheCredsMax 50
65 65 </Location>
66 66
67 67 To be able to browse repository inside redmine, you must add something
68 68 like that :
69 69
70 70 <Location /svn-private>
71 71 DAV svn
72 72 SVNParentPath "/var/svn"
73 73 Order deny,allow
74 74 Deny from all
75 75 # only allow reading orders
76 76 <Limit GET PROPFIND OPTIONS REPORT>
77 77 Allow from redmine.server.ip
78 78 </Limit>
79 79 </Location>
80 80
81 81 and you will have to use this reposman.rb command line to create repository :
82 82
83 83 reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
84 84
85 85 =head1 MIGRATION FROM OLDER RELEASES
86 86
87 87 If you use an older reposman.rb (r860 or before), you need to change
88 88 rights on repositories to allow the apache user to read and write
89 89 S<them :>
90 90
91 91 sudo chown -R www-data /var/svn/*
92 92 sudo chmod -R u+w /var/svn/*
93 93
94 94 And you need to upgrade at least reposman.rb (after r860).
95 95
96 96 =cut
97 97
98 98 use strict;
99 99 use warnings FATAL => 'all', NONFATAL => 'redefine';
100 100
101 101 use DBI;
102 102 use Digest::SHA1;
103 103 # optional module for LDAP authentication
104 104 my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
105 105
106 106 use Apache2::Module;
107 107 use Apache2::Access;
108 108 use Apache2::ServerRec qw();
109 109 use Apache2::RequestRec qw();
110 110 use Apache2::RequestUtil qw();
111 111 use Apache2::Const qw(:common :override :cmd_how);
112 112 use APR::Pool ();
113 113 use APR::Table ();
114 114
115 115 # use Apache2::Directive qw();
116 116
117 117 my @directives = (
118 118 {
119 119 name => 'RedmineDSN',
120 120 req_override => OR_AUTHCFG,
121 121 args_how => TAKE1,
122 122 errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
123 123 },
124 124 {
125 125 name => 'RedmineDbUser',
126 126 req_override => OR_AUTHCFG,
127 127 args_how => TAKE1,
128 128 },
129 129 {
130 130 name => 'RedmineDbPass',
131 131 req_override => OR_AUTHCFG,
132 132 args_how => TAKE1,
133 133 },
134 134 {
135 135 name => 'RedmineDbWhereClause',
136 136 req_override => OR_AUTHCFG,
137 137 args_how => TAKE1,
138 138 },
139 139 {
140 140 name => 'RedmineCacheCredsMax',
141 141 req_override => OR_AUTHCFG,
142 142 args_how => TAKE1,
143 143 errmsg => 'RedmineCacheCredsMax must be decimal number',
144 144 },
145 145 );
146 146
147 147 sub RedmineDSN {
148 148 my ($self, $parms, $arg) = @_;
149 149 $self->{RedmineDSN} = $arg;
150 150 my $query = "SELECT
151 hashed_password, auth_source_id
152 FROM members, projects, users
151 hashed_password, auth_source_id, permissions
152 FROM members, projects, users, roles
153 153 WHERE
154 154 projects.id=members.project_id
155 155 AND users.id=members.user_id
156 AND roles.id=members.role_id
156 157 AND users.status=1
157 158 AND login=?
158 159 AND identifier=? ";
159 160 $self->{RedmineQuery} = trim($query);
160 161 }
161 162 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
162 163 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
163 164 sub RedmineDbWhereClause {
164 165 my ($self, $parms, $arg) = @_;
165 166 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
166 167 }
167 168
168 169 sub RedmineCacheCredsMax {
169 170 my ($self, $parms, $arg) = @_;
170 171 if ($arg) {
171 172 $self->{RedmineCachePool} = APR::Pool->new;
172 173 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
173 174 $self->{RedmineCacheCredsCount} = 0;
174 175 $self->{RedmineCacheCredsMax} = $arg;
175 176 }
176 177 }
177 178
178 179 sub trim {
179 180 my $string = shift;
180 181 $string =~ s/\s{2,}/ /g;
181 182 return $string;
182 183 }
183 184
184 185 sub set_val {
185 186 my ($key, $self, $parms, $arg) = @_;
186 187 $self->{$key} = $arg;
187 188 }
188 189
189 190 Apache2::Module::add(__PACKAGE__, \@directives);
190 191
191 192
192 193 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
193 194
194 195 sub access_handler {
195 196 my $r = shift;
196 197
197 198 unless ($r->some_auth_required) {
198 199 $r->log_reason("No authentication has been configured");
199 200 return FORBIDDEN;
200 201 }
201 202
202 203 my $method = $r->method;
203 204 return OK unless defined $read_only_methods{$method};
204 205
205 206 my $project_id = get_project_identifier($r);
206 207
207 208 $r->set_handlers(PerlAuthenHandler => [\&OK])
208 209 if is_public_project($project_id, $r);
209 210
210 211 return OK
211 212 }
212 213
213 214 sub authen_handler {
214 215 my $r = shift;
215 216
216 217 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
217 218 return $res unless $res == OK;
218 219
219 220 if (is_member($r->user, $redmine_pass, $r)) {
220 221 return OK;
221 222 } else {
222 223 $r->note_auth_failure();
223 224 return AUTH_REQUIRED;
224 225 }
225 226 }
226 227
227 228 sub is_public_project {
228 229 my $project_id = shift;
229 230 my $r = shift;
230 231
231 232 my $dbh = connect_database($r);
232 233 my $sth = $dbh->prepare(
233 234 "SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
234 235 );
235 236
236 237 $sth->execute($project_id);
237 238 my $ret = $sth->fetchrow_array ? 1 : 0;
238 239 $sth->finish();
239 240 $dbh->disconnect();
240 241
241 242 $ret;
242 243 }
243 244
244 245 # perhaps we should use repository right (other read right) to check public access.
245 246 # it could be faster BUT it doesn't work for the moment.
246 247 # sub is_public_project_by_file {
247 248 # my $project_id = shift;
248 249 # my $r = shift;
249 250
250 251 # my $tree = Apache2::Directive::conftree();
251 252 # my $node = $tree->lookup('Location', $r->location);
252 253 # my $hash = $node->as_hash;
253 254
254 255 # my $svnparentpath = $hash->{SVNParentPath};
255 256 # my $repos_path = $svnparentpath . "/" . $project_id;
256 257 # return 1 if (stat($repos_path))[2] & 00007;
257 258 # }
258 259
259 260 sub is_member {
260 261 my $redmine_user = shift;
261 262 my $redmine_pass = shift;
262 263 my $r = shift;
263 264
264 265 my $dbh = connect_database($r);
265 266 my $project_id = get_project_identifier($r);
266 267
267 268 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
268 269
269 270 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
270 271 my $usrprojpass;
271 272 if ($cfg->{RedmineCacheCredsMax}) {
272 273 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id);
273 274 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
274 275 }
275 276 my $query = $cfg->{RedmineQuery};
276 277 my $sth = $dbh->prepare($query);
277 278 $sth->execute($redmine_user, $project_id);
278 279
279 280 my $ret;
280 while (my @row = $sth->fetchrow_array) {
281 unless ($row[1]) {
282 if ($row[0] eq $pass_digest) {
281 while (my ($hashed_password, $auth_source_id, $permissions) = $sth->fetchrow_array) {
282
283 unless ($auth_source_id) {
284 my $method = $r->method;
285 if ($hashed_password eq $pass_digest && (defined $read_only_methods{$method} || $permissions =~ /:commit_access/) ) {
283 286 $ret = 1;
284 287 last;
285 288 }
286 289 } elsif ($CanUseLDAPAuth) {
287 290 my $sthldap = $dbh->prepare(
288 291 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
289 292 );
290 $sthldap->execute($row[1]);
293 $sthldap->execute($auth_source_id);
291 294 while (my @rowldap = $sthldap->fetchrow_array) {
292 295 my $ldap = Authen::Simple::LDAP->new(
293 296 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
294 297 port => $rowldap[1],
295 298 basedn => $rowldap[5],
296 299 binddn => $rowldap[3] ? $rowldap[3] : "",
297 300 bindpw => $rowldap[4] ? $rowldap[4] : "",
298 301 filter => "(".$rowldap[6]."=%s)"
299 302 );
300 303 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
301 304 }
302 305 $sthldap->finish();
303 306 }
304 307 }
305 308 $sth->finish();
306 309 $dbh->disconnect();
307 310
308 311 if ($cfg->{RedmineCacheCredsMax} and $ret) {
309 312 if (defined $usrprojpass) {
310 313 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
311 314 } else {
312 315 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
313 316 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
314 317 $cfg->{RedmineCacheCredsCount}++;
315 318 } else {
316 319 $cfg->{RedmineCacheCreds}->clear();
317 320 $cfg->{RedmineCacheCredsCount} = 0;
318 321 }
319 322 }
320 323 }
321 324
322 325 $ret;
323 326 }
324 327
325 328 sub get_project_identifier {
326 329 my $r = shift;
327 330
328 331 my $location = $r->location;
329 332 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
330 333 $identifier;
331 334 }
332 335
333 336 sub connect_database {
334 337 my $r = shift;
335 338
336 339 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
337 340 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
338 341 }
339 342
340 343 1;
@@ -1,149 +1,150
1 1 require 'redmine/access_control'
2 2 require 'redmine/menu_manager'
3 3 require 'redmine/activity'
4 4 require 'redmine/mime_type'
5 5 require 'redmine/core_ext'
6 6 require 'redmine/themes'
7 7 require 'redmine/hook'
8 8 require 'redmine/plugin'
9 9
10 10 begin
11 11 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
12 12 rescue LoadError
13 13 # RMagick is not available
14 14 end
15 15
16 16 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git Filesystem )
17 17
18 18 # Permissions
19 19 Redmine::AccessControl.map do |map|
20 20 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
21 21 map.permission :search_project, {:search => :index}, :public => true
22 22 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
23 23 map.permission :select_project_modules, {:projects => :modules}, :require => :member
24 24 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
25 25 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
26 26
27 27 map.project_module :issue_tracking do |map|
28 28 # Issue categories
29 29 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
30 30 # Issues
31 31 map.permission :view_issues, {:projects => [:changelog, :roadmap],
32 32 :issues => [:index, :changes, :show, :context_menu],
33 33 :versions => [:show, :status_by],
34 34 :queries => :index,
35 35 :reports => :issue_report}, :public => true
36 36 map.permission :add_issues, {:issues => :new}
37 37 map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :destroy_attachment]}
38 38 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
39 39 map.permission :add_issue_notes, {:issues => [:edit, :reply]}
40 40 map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
41 41 map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
42 42 map.permission :move_issues, {:issues => :move}, :require => :loggedin
43 43 map.permission :delete_issues, {:issues => :destroy}, :require => :member
44 44 # Queries
45 45 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
46 46 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
47 47 # Gantt & calendar
48 48 map.permission :view_gantt, :projects => :gantt
49 49 map.permission :view_calendar, :projects => :calendar
50 50 # Watchers
51 51 map.permission :view_issue_watchers, {}
52 52 map.permission :add_issue_watchers, {:watchers => :new}
53 53 end
54 54
55 55 map.project_module :time_tracking do |map|
56 56 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
57 57 map.permission :view_time_entries, :timelog => [:details, :report]
58 58 map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member
59 59 map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin
60 60 end
61 61
62 62 map.project_module :news do |map|
63 63 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
64 64 map.permission :view_news, {:news => [:index, :show]}, :public => true
65 65 map.permission :comment_news, {:news => :add_comment}
66 66 end
67 67
68 68 map.project_module :documents do |map|
69 69 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
70 70 map.permission :view_documents, :documents => [:index, :show, :download]
71 71 end
72 72
73 73 map.project_module :files do |map|
74 74 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
75 75 map.permission :view_files, :projects => :list_files, :versions => :download
76 76 end
77 77
78 78 map.project_module :wiki do |map|
79 79 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
80 80 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
81 81 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
82 82 map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :annotate, :special]
83 83 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
84 84 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
85 85 end
86 86
87 87 map.project_module :repository do |map|
88 88 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
89 89 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
90 90 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
91 map.permission :commit_access, {}
91 92 end
92 93
93 94 map.project_module :boards do |map|
94 95 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
95 96 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
96 97 map.permission :add_messages, {:messages => [:new, :reply, :quote]}
97 98 map.permission :edit_messages, {:messages => :edit}, :require => :member
98 99 map.permission :delete_messages, {:messages => :destroy}, :require => :member
99 100 end
100 101 end
101 102
102 103 Redmine::MenuManager.map :top_menu do |menu|
103 104 menu.push :home, :home_path, :html => { :class => 'home' }
104 105 menu.push :my_page, { :controller => 'my', :action => 'page' }, :html => { :class => 'mypage' }, :if => Proc.new { User.current.logged? }
105 106 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural, :html => { :class => 'projects' }
106 107 menu.push :administration, { :controller => 'admin', :action => 'index' }, :html => { :class => 'admin' }, :if => Proc.new { User.current.admin? }, :last => true
107 108 menu.push :help, Redmine::Info.help_url, :html => { :class => 'help' }, :last => true
108 109 end
109 110
110 111 Redmine::MenuManager.map :account_menu do |menu|
111 112 menu.push :login, :signin_path, :html => { :class => 'login' }, :if => Proc.new { !User.current.logged? }
112 113 menu.push :register, { :controller => 'account', :action => 'register' }, :html => { :class => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
113 114 menu.push :my_account, { :controller => 'my', :action => 'account' }, :html => { :class => 'myaccount' }, :if => Proc.new { User.current.logged? }
114 115 menu.push :logout, :signout_path, :html => { :class => 'logout' }, :if => Proc.new { User.current.logged? }
115 116 end
116 117
117 118 Redmine::MenuManager.map :application_menu do |menu|
118 119 # Empty
119 120 end
120 121
121 122 Redmine::MenuManager.map :project_menu do |menu|
122 123 menu.push :overview, { :controller => 'projects', :action => 'show' }
123 124 menu.push :activity, { :controller => 'projects', :action => 'activity' }
124 125 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
125 126 :if => Proc.new { |p| p.versions.any? }
126 127 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
127 128 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
128 129 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
129 130 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
130 131 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
131 132 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
132 133 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
133 134 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
134 135 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
135 136 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_attachment_plural
136 137 menu.push :repository, { :controller => 'repositories', :action => 'show' },
137 138 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
138 139 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
139 140 end
140 141
141 142 Redmine::Activity.map do |activity|
142 143 activity.register :issues, :class_name => %w(Issue Journal)
143 144 activity.register :changesets
144 145 activity.register :news
145 146 activity.register :documents, :class_name => %w(Document Attachment)
146 147 activity.register :files, :class_name => 'Attachment'
147 148 activity.register :wiki_pages, :class_name => 'WikiContent::Version', :default => false
148 149 activity.register :messages, :default => false
149 150 end
@@ -1,169 +1,170
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 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 module Redmine
19 19 module DefaultData
20 20 class DataAlreadyLoaded < Exception; end
21 21
22 22 module Loader
23 23 include GLoc
24 24
25 25 class << self
26 26 # Returns true if no data is already loaded in the database
27 27 # otherwise false
28 28 def no_data?
29 29 !Role.find(:first, :conditions => {:builtin => 0}) &&
30 30 !Tracker.find(:first) &&
31 31 !IssueStatus.find(:first) &&
32 32 !Enumeration.find(:first)
33 33 end
34 34
35 35 # Loads the default data
36 36 # Raises a RecordNotSaved exception if something goes wrong
37 37 def load(lang=nil)
38 38 raise DataAlreadyLoaded.new("Some configuration data is already loaded.") unless no_data?
39 39 set_language_if_valid(lang)
40 40
41 41 Role.transaction do
42 42 # Roles
43 43 manager = Role.create! :name => l(:default_role_manager),
44 44 :position => 1
45 45 manager.permissions = manager.setable_permissions.collect {|p| p.name}
46 46 manager.save!
47 47
48 48 developper = Role.create! :name => l(:default_role_developper),
49 49 :position => 2,
50 50 :permissions => [:manage_versions,
51 51 :manage_categories,
52 52 :add_issues,
53 53 :edit_issues,
54 54 :manage_issue_relations,
55 55 :add_issue_notes,
56 56 :save_queries,
57 57 :view_gantt,
58 58 :view_calendar,
59 59 :log_time,
60 60 :view_time_entries,
61 61 :comment_news,
62 62 :view_documents,
63 63 :view_wiki_pages,
64 64 :edit_wiki_pages,
65 65 :delete_wiki_pages,
66 66 :add_messages,
67 67 :view_files,
68 68 :manage_files,
69 69 :browse_repository,
70 :view_changesets]
70 :view_changesets,
71 :commit_access]
71 72
72 73 reporter = Role.create! :name => l(:default_role_reporter),
73 74 :position => 3,
74 75 :permissions => [:add_issues,
75 76 :add_issue_notes,
76 77 :save_queries,
77 78 :view_gantt,
78 79 :view_calendar,
79 80 :log_time,
80 81 :view_time_entries,
81 82 :comment_news,
82 83 :view_documents,
83 84 :view_wiki_pages,
84 85 :add_messages,
85 86 :view_files,
86 87 :browse_repository,
87 88 :view_changesets]
88 89
89 90 Role.non_member.update_attribute :permissions, [:add_issues,
90 91 :add_issue_notes,
91 92 :save_queries,
92 93 :view_gantt,
93 94 :view_calendar,
94 95 :view_time_entries,
95 96 :comment_news,
96 97 :view_documents,
97 98 :view_wiki_pages,
98 99 :add_messages,
99 100 :view_files,
100 101 :browse_repository,
101 102 :view_changesets]
102 103
103 104 Role.anonymous.update_attribute :permissions, [:view_gantt,
104 105 :view_calendar,
105 106 :view_time_entries,
106 107 :view_documents,
107 108 :view_wiki_pages,
108 109 :view_files,
109 110 :browse_repository,
110 111 :view_changesets]
111 112
112 113 # Trackers
113 114 Tracker.create!(:name => l(:default_tracker_bug), :is_in_chlog => true, :is_in_roadmap => false, :position => 1)
114 115 Tracker.create!(:name => l(:default_tracker_feature), :is_in_chlog => true, :is_in_roadmap => true, :position => 2)
115 116 Tracker.create!(:name => l(:default_tracker_support), :is_in_chlog => false, :is_in_roadmap => false, :position => 3)
116 117
117 118 # Issue statuses
118 119 new = IssueStatus.create!(:name => l(:default_issue_status_new), :is_closed => false, :is_default => true, :position => 1)
119 120 assigned = IssueStatus.create!(:name => l(:default_issue_status_assigned), :is_closed => false, :is_default => false, :position => 2)
120 121 resolved = IssueStatus.create!(:name => l(:default_issue_status_resolved), :is_closed => false, :is_default => false, :position => 3)
121 122 feedback = IssueStatus.create!(:name => l(:default_issue_status_feedback), :is_closed => false, :is_default => false, :position => 4)
122 123 closed = IssueStatus.create!(:name => l(:default_issue_status_closed), :is_closed => true, :is_default => false, :position => 5)
123 124 rejected = IssueStatus.create!(:name => l(:default_issue_status_rejected), :is_closed => true, :is_default => false, :position => 6)
124 125
125 126 # Workflow
126 127 Tracker.find(:all).each { |t|
127 128 IssueStatus.find(:all).each { |os|
128 129 IssueStatus.find(:all).each { |ns|
129 130 Workflow.create!(:tracker_id => t.id, :role_id => manager.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
130 131 }
131 132 }
132 133 }
133 134
134 135 Tracker.find(:all).each { |t|
135 136 [new, assigned, resolved, feedback].each { |os|
136 137 [assigned, resolved, feedback, closed].each { |ns|
137 138 Workflow.create!(:tracker_id => t.id, :role_id => developper.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
138 139 }
139 140 }
140 141 }
141 142
142 143 Tracker.find(:all).each { |t|
143 144 [new, assigned, resolved, feedback].each { |os|
144 145 [closed].each { |ns|
145 146 Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => os.id, :new_status_id => ns.id) unless os == ns
146 147 }
147 148 }
148 149 Workflow.create!(:tracker_id => t.id, :role_id => reporter.id, :old_status_id => resolved.id, :new_status_id => feedback.id)
149 150 }
150 151
151 152 # Enumerations
152 153 Enumeration.create!(:opt => "DCAT", :name => l(:default_doc_category_user), :position => 1)
153 154 Enumeration.create!(:opt => "DCAT", :name => l(:default_doc_category_tech), :position => 2)
154 155
155 156 Enumeration.create!(:opt => "IPRI", :name => l(:default_priority_low), :position => 1)
156 157 Enumeration.create!(:opt => "IPRI", :name => l(:default_priority_normal), :position => 2, :is_default => true)
157 158 Enumeration.create!(:opt => "IPRI", :name => l(:default_priority_high), :position => 3)
158 159 Enumeration.create!(:opt => "IPRI", :name => l(:default_priority_urgent), :position => 4)
159 160 Enumeration.create!(:opt => "IPRI", :name => l(:default_priority_immediate), :position => 5)
160 161
161 162 Enumeration.create!(:opt => "ACTI", :name => l(:default_activity_design), :position => 1)
162 163 Enumeration.create!(:opt => "ACTI", :name => l(:default_activity_development), :position => 2)
163 164 end
164 165 true
165 166 end
166 167 end
167 168 end
168 169 end
169 170 end
@@ -1,33 +1,53
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class RoleTest < Test::Unit::TestCase
21 21 fixtures :roles, :workflows
22 22
23 23 def test_copy_workflows
24 24 source = Role.find(1)
25 25 assert_equal 90, source.workflows.size
26 26
27 27 target = Role.new(:name => 'Target')
28 28 assert target.save
29 29 target.workflows.copy(source)
30 30 target.reload
31 31 assert_equal 90, target.workflows.size
32 32 end
33
34 def test_add_permission
35 role = Role.find(1)
36 size = role.permissions.size
37 role.add_permission!("apermission", "anotherpermission")
38 role.reload
39 assert role.permissions.include?(:anotherpermission)
40 assert_equal size + 2, role.permissions.size
41 end
42
43 def test_remove_permission
44 role = Role.find(1)
45 size = role.permissions.size
46 perm = role.permissions[0..1]
47 role.remove_permission!(*perm)
48 role.reload
49 assert ! role.permissions.include?(perm[0])
50 assert_equal size - 2, role.permissions.size
51 end
52
33 53 end
General Comments 0
You need to be logged in to leave comments. Login now