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