##// END OF EJS Templates
Fixed: Redmine.pm potential security issue with cache credential enabled and subversion (#9567)....
Jean-Philippe Lang -
r7689:38089d9a5606
parent child
Show More
@@ -1,427 +1,429
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, salt, auth_source_id, permissions
151 hashed_password, salt, auth_source_id, permissions
152 FROM members, projects, users, roles, member_roles
152 FROM members, projects, users, roles, member_roles
153 WHERE
153 WHERE
154 projects.id=members.project_id
154 projects.id=members.project_id
155 AND member_roles.member_id=members.id
155 AND member_roles.member_id=members.id
156 AND users.id=members.user_id
156 AND users.id=members.user_id
157 AND roles.id=member_roles.role_id
157 AND roles.id=member_roles.role_id
158 AND users.status=1
158 AND users.status=1
159 AND login=?
159 AND login=?
160 AND identifier=? ";
160 AND identifier=? ";
161 $self->{RedmineQuery} = trim($query);
161 $self->{RedmineQuery} = trim($query);
162 }
162 }
163
163
164 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
164 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
165 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
165 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
166 sub RedmineDbWhereClause {
166 sub RedmineDbWhereClause {
167 my ($self, $parms, $arg) = @_;
167 my ($self, $parms, $arg) = @_;
168 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
168 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
169 }
169 }
170
170
171 sub RedmineCacheCredsMax {
171 sub RedmineCacheCredsMax {
172 my ($self, $parms, $arg) = @_;
172 my ($self, $parms, $arg) = @_;
173 if ($arg) {
173 if ($arg) {
174 $self->{RedmineCachePool} = APR::Pool->new;
174 $self->{RedmineCachePool} = APR::Pool->new;
175 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
175 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
176 $self->{RedmineCacheCredsCount} = 0;
176 $self->{RedmineCacheCredsCount} = 0;
177 $self->{RedmineCacheCredsMax} = $arg;
177 $self->{RedmineCacheCredsMax} = $arg;
178 }
178 }
179 }
179 }
180
180
181 sub trim {
181 sub trim {
182 my $string = shift;
182 my $string = shift;
183 $string =~ s/\s{2,}/ /g;
183 $string =~ s/\s{2,}/ /g;
184 return $string;
184 return $string;
185 }
185 }
186
186
187 sub set_val {
187 sub set_val {
188 my ($key, $self, $parms, $arg) = @_;
188 my ($key, $self, $parms, $arg) = @_;
189 $self->{$key} = $arg;
189 $self->{$key} = $arg;
190 }
190 }
191
191
192 Apache2::Module::add(__PACKAGE__, \@directives);
192 Apache2::Module::add(__PACKAGE__, \@directives);
193
193
194
194
195 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
195 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
196
196
197 sub access_handler {
197 sub access_handler {
198 my $r = shift;
198 my $r = shift;
199
199
200 unless ($r->some_auth_required) {
200 unless ($r->some_auth_required) {
201 $r->log_reason("No authentication has been configured");
201 $r->log_reason("No authentication has been configured");
202 return FORBIDDEN;
202 return FORBIDDEN;
203 }
203 }
204
204
205 my $method = $r->method;
205 my $method = $r->method;
206 return OK unless defined $read_only_methods{$method};
206 return OK unless defined $read_only_methods{$method};
207
207
208 return OK if is_authentication_forced($r);
208 return OK if is_authentication_forced($r);
209
209
210 my $project_id = get_project_identifier($r);
210 my $project_id = get_project_identifier($r);
211
211
212 $r->set_handlers(PerlAuthenHandler => [\&OK])
212 $r->set_handlers(PerlAuthenHandler => [\&OK])
213 if is_public_project($project_id, $r) && anonymous_role_allows_browse_repository($r);
213 if is_public_project($project_id, $r) && anonymous_role_allows_browse_repository($r);
214
214
215 return OK
215 return OK
216 }
216 }
217
217
218 sub authen_handler {
218 sub authen_handler {
219 my $r = shift;
219 my $r = shift;
220
220
221 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
221 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
222 return $res unless $res == OK;
222 return $res unless $res == OK;
223
223
224 my $project_id = get_project_identifier($r);
224 my $project_id = get_project_identifier($r);
225 my $method = $r->method;
225 my $method = $r->method;
226 if (defined $read_only_methods{$method} && is_public_project($project_id, $r) && non_member_role_allows_browse_repository($r)) {
226 if (defined $read_only_methods{$method} && is_public_project($project_id, $r) && non_member_role_allows_browse_repository($r)) {
227 return OK;
227 return OK;
228 }
228 }
229
229
230 if (is_member($r->user, $redmine_pass, $r)) {
230 if (is_member($r->user, $redmine_pass, $r)) {
231 return OK;
231 return OK;
232 } else {
232 } else {
233 $r->note_auth_failure();
233 $r->note_auth_failure();
234 return AUTH_REQUIRED;
234 return AUTH_REQUIRED;
235 }
235 }
236 }
236 }
237
237
238 # check if authentication is forced
238 # check if authentication is forced
239 sub is_authentication_forced {
239 sub is_authentication_forced {
240 my $r = shift;
240 my $r = shift;
241
241
242 my $dbh = connect_database($r);
242 my $dbh = connect_database($r);
243 my $sth = $dbh->prepare(
243 my $sth = $dbh->prepare(
244 "SELECT value FROM settings where settings.name = 'login_required';"
244 "SELECT value FROM settings where settings.name = 'login_required';"
245 );
245 );
246
246
247 $sth->execute();
247 $sth->execute();
248 my $ret = 0;
248 my $ret = 0;
249 if (my @row = $sth->fetchrow_array) {
249 if (my @row = $sth->fetchrow_array) {
250 if ($row[0] eq "1" || $row[0] eq "t") {
250 if ($row[0] eq "1" || $row[0] eq "t") {
251 $ret = 1;
251 $ret = 1;
252 }
252 }
253 }
253 }
254 $sth->finish();
254 $sth->finish();
255 undef $sth;
255 undef $sth;
256
256
257 $dbh->disconnect();
257 $dbh->disconnect();
258 undef $dbh;
258 undef $dbh;
259
259
260 $ret;
260 $ret;
261 }
261 }
262
262
263 sub is_public_project {
263 sub is_public_project {
264 my $project_id = shift;
264 my $project_id = shift;
265 my $r = shift;
265 my $r = shift;
266
266
267 my $dbh = connect_database($r);
267 my $dbh = connect_database($r);
268 my $sth = $dbh->prepare(
268 my $sth = $dbh->prepare(
269 "SELECT is_public FROM projects WHERE projects.identifier = ?;"
269 "SELECT is_public FROM projects WHERE projects.identifier = ?;"
270 );
270 );
271
271
272 $sth->execute($project_id);
272 $sth->execute($project_id);
273 my $ret = 0;
273 my $ret = 0;
274 if (my @row = $sth->fetchrow_array) {
274 if (my @row = $sth->fetchrow_array) {
275 if ($row[0] eq "1" || $row[0] eq "t") {
275 if ($row[0] eq "1" || $row[0] eq "t") {
276 $ret = 1;
276 $ret = 1;
277 }
277 }
278 }
278 }
279 $sth->finish();
279 $sth->finish();
280 undef $sth;
280 undef $sth;
281 $dbh->disconnect();
281 $dbh->disconnect();
282 undef $dbh;
282 undef $dbh;
283
283
284 $ret;
284 $ret;
285 }
285 }
286
286
287 sub system_role_allows_browse_repository {
287 sub system_role_allows_browse_repository {
288 my $r = shift;
288 my $r = shift;
289 my $system_role = shift;
289 my $system_role = shift;
290
290
291 my $dbh = connect_database($r);
291 my $dbh = connect_database($r);
292 my $sth = $dbh->prepare(
292 my $sth = $dbh->prepare(
293 "SELECT permissions FROM roles WHERE builtin = ?;"
293 "SELECT permissions FROM roles WHERE builtin = ?;"
294 );
294 );
295
295
296 $sth->execute($system_role);
296 $sth->execute($system_role);
297 my $ret = 0;
297 my $ret = 0;
298 if (my @row = $sth->fetchrow_array) {
298 if (my @row = $sth->fetchrow_array) {
299 if ($row[0] =~ /:browse_repository/) {
299 if ($row[0] =~ /:browse_repository/) {
300 $ret = 1;
300 $ret = 1;
301 }
301 }
302 }
302 }
303 $sth->finish();
303 $sth->finish();
304 undef $sth;
304 undef $sth;
305 $dbh->disconnect();
305 $dbh->disconnect();
306 undef $dbh;
306 undef $dbh;
307
307
308 $ret;
308 $ret;
309 }
309 }
310
310
311 sub non_member_role_allows_browse_repository {
311 sub non_member_role_allows_browse_repository {
312 my $r = shift;
312 my $r = shift;
313 my $ret = system_role_allows_browse_repository($r, 1);
313 my $ret = system_role_allows_browse_repository($r, 1);
314 $ret;
314 $ret;
315 }
315 }
316
316
317 sub anonymous_role_allows_browse_repository {
317 sub anonymous_role_allows_browse_repository {
318 my $r = shift;
318 my $r = shift;
319 my $ret = system_role_allows_browse_repository($r, 2);
319 my $ret = system_role_allows_browse_repository($r, 2);
320 $ret;
320 $ret;
321 }
321 }
322
322
323 # perhaps we should use repository right (other read right) to check public access.
323 # perhaps we should use repository right (other read right) to check public access.
324 # it could be faster BUT it doesn't work for the moment.
324 # it could be faster BUT it doesn't work for the moment.
325 # sub is_public_project_by_file {
325 # sub is_public_project_by_file {
326 # my $project_id = shift;
326 # my $project_id = shift;
327 # my $r = shift;
327 # my $r = shift;
328
328
329 # my $tree = Apache2::Directive::conftree();
329 # my $tree = Apache2::Directive::conftree();
330 # my $node = $tree->lookup('Location', $r->location);
330 # my $node = $tree->lookup('Location', $r->location);
331 # my $hash = $node->as_hash;
331 # my $hash = $node->as_hash;
332
332
333 # my $svnparentpath = $hash->{SVNParentPath};
333 # my $svnparentpath = $hash->{SVNParentPath};
334 # my $repos_path = $svnparentpath . "/" . $project_id;
334 # my $repos_path = $svnparentpath . "/" . $project_id;
335 # return 1 if (stat($repos_path))[2] & 00007;
335 # return 1 if (stat($repos_path))[2] & 00007;
336 # }
336 # }
337
337
338 sub is_member {
338 sub is_member {
339 my $redmine_user = shift;
339 my $redmine_user = shift;
340 my $redmine_pass = shift;
340 my $redmine_pass = shift;
341 my $r = shift;
341 my $r = shift;
342
342
343 my $dbh = connect_database($r);
343 my $dbh = connect_database($r);
344 my $project_id = get_project_identifier($r);
344 my $project_id = get_project_identifier($r);
345
345
346 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
346 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
347
347
348 my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W";
349
348 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
350 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
349 my $usrprojpass;
351 my $usrprojpass;
350 if ($cfg->{RedmineCacheCredsMax}) {
352 if ($cfg->{RedmineCacheCredsMax}) {
351 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id);
353 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
352 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
354 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
353 }
355 }
354 my $query = $cfg->{RedmineQuery};
356 my $query = $cfg->{RedmineQuery};
355 my $sth = $dbh->prepare($query);
357 my $sth = $dbh->prepare($query);
356 $sth->execute($redmine_user, $project_id);
358 $sth->execute($redmine_user, $project_id);
357
359
358 my $ret;
360 my $ret;
359 while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
361 while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
360
362
361 unless ($auth_source_id) {
363 unless ($auth_source_id) {
362 my $method = $r->method;
364 my $method = $r->method;
363 my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
365 my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
364 if ($hashed_password eq $salted_password && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
366 if ($hashed_password eq $salted_password && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
365 $ret = 1;
367 $ret = 1;
366 last;
368 last;
367 }
369 }
368 } elsif ($CanUseLDAPAuth) {
370 } elsif ($CanUseLDAPAuth) {
369 my $sthldap = $dbh->prepare(
371 my $sthldap = $dbh->prepare(
370 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
372 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
371 );
373 );
372 $sthldap->execute($auth_source_id);
374 $sthldap->execute($auth_source_id);
373 while (my @rowldap = $sthldap->fetchrow_array) {
375 while (my @rowldap = $sthldap->fetchrow_array) {
374 my $ldap = Authen::Simple::LDAP->new(
376 my $ldap = Authen::Simple::LDAP->new(
375 host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]:$rowldap[1]" : $rowldap[0],
377 host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]:$rowldap[1]" : $rowldap[0],
376 port => $rowldap[1],
378 port => $rowldap[1],
377 basedn => $rowldap[5],
379 basedn => $rowldap[5],
378 binddn => $rowldap[3] ? $rowldap[3] : "",
380 binddn => $rowldap[3] ? $rowldap[3] : "",
379 bindpw => $rowldap[4] ? $rowldap[4] : "",
381 bindpw => $rowldap[4] ? $rowldap[4] : "",
380 filter => "(".$rowldap[6]."=%s)"
382 filter => "(".$rowldap[6]."=%s)"
381 );
383 );
382 my $method = $r->method;
384 my $method = $r->method;
383 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
385 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
384
386
385 }
387 }
386 $sthldap->finish();
388 $sthldap->finish();
387 undef $sthldap;
389 undef $sthldap;
388 }
390 }
389 }
391 }
390 $sth->finish();
392 $sth->finish();
391 undef $sth;
393 undef $sth;
392 $dbh->disconnect();
394 $dbh->disconnect();
393 undef $dbh;
395 undef $dbh;
394
396
395 if ($cfg->{RedmineCacheCredsMax} and $ret) {
397 if ($cfg->{RedmineCacheCredsMax} and $ret) {
396 if (defined $usrprojpass) {
398 if (defined $usrprojpass) {
397 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
399 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
398 } else {
400 } else {
399 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
401 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
400 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
402 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
401 $cfg->{RedmineCacheCredsCount}++;
403 $cfg->{RedmineCacheCredsCount}++;
402 } else {
404 } else {
403 $cfg->{RedmineCacheCreds}->clear();
405 $cfg->{RedmineCacheCreds}->clear();
404 $cfg->{RedmineCacheCredsCount} = 0;
406 $cfg->{RedmineCacheCredsCount} = 0;
405 }
407 }
406 }
408 }
407 }
409 }
408
410
409 $ret;
411 $ret;
410 }
412 }
411
413
412 sub get_project_identifier {
414 sub get_project_identifier {
413 my $r = shift;
415 my $r = shift;
414
416
415 my $location = $r->location;
417 my $location = $r->location;
416 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
418 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
417 $identifier;
419 $identifier;
418 }
420 }
419
421
420 sub connect_database {
422 sub connect_database {
421 my $r = shift;
423 my $r = shift;
422
424
423 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
425 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
424 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
426 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
425 }
427 }
426
428
427 1;
429 1;
General Comments 0
You need to be logged in to leave comments. Login now