comparison extra/svn/Redmine.pm @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents 051f544170fe
children 5f33065ddc4b
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
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
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 projects, users, roles
153 WHERE 153 WHERE
154 projects.id=members.project_id 154 users.login=?
155 AND member_roles.member_id=members.id 155 AND projects.identifier=?
156 AND users.id=members.user_id
157 AND roles.id=member_roles.role_id
158 AND users.status=1 156 AND users.status=1
159 AND login=? 157 AND (
160 AND identifier=? "; 158 roles.id IN (SELECT member_roles.role_id FROM members, member_roles WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id)
159 OR
160 (roles.builtin=1 AND cast(projects.is_public as CHAR) IN ('t', '1'))
161 ) ";
161 $self->{RedmineQuery} = trim($query); 162 $self->{RedmineQuery} = trim($query);
162 } 163 }
163 164
164 sub RedmineDbUser { set_val('RedmineDbUser', @_); } 165 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
165 sub RedmineDbPass { set_val('RedmineDbPass', @_); } 166 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
166 sub RedmineDbWhereClause { 167 sub RedmineDbWhereClause {
167 my ($self, $parms, $arg) = @_; 168 my ($self, $parms, $arg) = @_;
168 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." "); 169 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
169 } 170 }
170 171
171 sub RedmineCacheCredsMax { 172 sub RedmineCacheCredsMax {
172 my ($self, $parms, $arg) = @_; 173 my ($self, $parms, $arg) = @_;
173 if ($arg) { 174 if ($arg) {
174 $self->{RedmineCachePool} = APR::Pool->new; 175 $self->{RedmineCachePool} = APR::Pool->new;
175 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg); 176 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
176 $self->{RedmineCacheCredsCount} = 0; 177 $self->{RedmineCacheCredsCount} = 0;
206 return OK unless defined $read_only_methods{$method}; 207 return OK unless defined $read_only_methods{$method};
207 208
208 my $project_id = get_project_identifier($r); 209 my $project_id = get_project_identifier($r);
209 210
210 $r->set_handlers(PerlAuthenHandler => [\&OK]) 211 $r->set_handlers(PerlAuthenHandler => [\&OK])
211 if is_public_project($project_id, $r); 212 if is_public_project($project_id, $r) && anonymous_role_allows_browse_repository($r);
212 213
213 return OK 214 return OK
214 } 215 }
215 216
216 sub authen_handler { 217 sub authen_handler {
217 my $r = shift; 218 my $r = shift;
218 219
219 my ($res, $redmine_pass) = $r->get_basic_auth_pw(); 220 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
220 return $res unless $res == OK; 221 return $res unless $res == OK;
221 222
222 if (is_member($r->user, $redmine_pass, $r)) { 223 if (is_member($r->user, $redmine_pass, $r)) {
223 return OK; 224 return OK;
224 } else { 225 } else {
225 $r->note_auth_failure(); 226 $r->note_auth_failure();
226 return AUTH_REQUIRED; 227 return AUTH_REQUIRED;
243 $ret = 1; 244 $ret = 1;
244 } 245 }
245 } 246 }
246 $sth->finish(); 247 $sth->finish();
247 undef $sth; 248 undef $sth;
248 249
249 $dbh->disconnect(); 250 $dbh->disconnect();
250 undef $dbh; 251 undef $dbh;
251 252
252 $ret; 253 $ret;
253 } 254 }
254 255
255 sub is_public_project { 256 sub is_public_project {
256 my $project_id = shift; 257 my $project_id = shift;
257 my $r = shift; 258 my $r = shift;
258 259
259 if (is_authentication_forced($r)) { 260 if (is_authentication_forced($r)) {
260 return 0; 261 return 0;
261 } 262 }
262 263
263 my $dbh = connect_database($r); 264 my $dbh = connect_database($r);
278 undef $dbh; 279 undef $dbh;
279 280
280 $ret; 281 $ret;
281 } 282 }
282 283
284 sub anonymous_role_allows_browse_repository {
285 my $r = shift;
286
287 my $dbh = connect_database($r);
288 my $sth = $dbh->prepare(
289 "SELECT permissions FROM roles WHERE builtin = 2;"
290 );
291
292 $sth->execute();
293 my $ret = 0;
294 if (my @row = $sth->fetchrow_array) {
295 if ($row[0] =~ /:browse_repository/) {
296 $ret = 1;
297 }
298 }
299 $sth->finish();
300 undef $sth;
301 $dbh->disconnect();
302 undef $dbh;
303
304 $ret;
305 }
306
283 # perhaps we should use repository right (other read right) to check public access. 307 # perhaps we should use repository right (other read right) to check public access.
284 # it could be faster BUT it doesn't work for the moment. 308 # it could be faster BUT it doesn't work for the moment.
285 # sub is_public_project_by_file { 309 # sub is_public_project_by_file {
286 # my $project_id = shift; 310 # my $project_id = shift;
287 # my $r = shift; 311 # my $r = shift;
303 my $dbh = connect_database($r); 327 my $dbh = connect_database($r);
304 my $project_id = get_project_identifier($r); 328 my $project_id = get_project_identifier($r);
305 329
306 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); 330 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
307 331
332 my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W";
333
308 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); 334 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
309 my $usrprojpass; 335 my $usrprojpass;
310 if ($cfg->{RedmineCacheCredsMax}) { 336 if ($cfg->{RedmineCacheCredsMax}) {
311 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id); 337 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
312 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest)); 338 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
313 } 339 }
314 my $query = $cfg->{RedmineQuery}; 340 my $query = $cfg->{RedmineQuery};
315 my $sth = $dbh->prepare($query); 341 my $sth = $dbh->prepare($query);
316 $sth->execute($redmine_user, $project_id); 342 $sth->execute($redmine_user, $project_id);
319 while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) { 345 while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
320 346
321 unless ($auth_source_id) { 347 unless ($auth_source_id) {
322 my $method = $r->method; 348 my $method = $r->method;
323 my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest); 349 my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
324 if ($hashed_password eq $salted_password && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) { 350 if ($hashed_password eq $salted_password && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
325 $ret = 1; 351 $ret = 1;
326 last; 352 last;
327 } 353 }
328 } elsif ($CanUseLDAPAuth) { 354 } elsif ($CanUseLDAPAuth) {
329 my $sthldap = $dbh->prepare( 355 my $sthldap = $dbh->prepare(
338 binddn => $rowldap[3] ? $rowldap[3] : "", 364 binddn => $rowldap[3] ? $rowldap[3] : "",
339 bindpw => $rowldap[4] ? $rowldap[4] : "", 365 bindpw => $rowldap[4] ? $rowldap[4] : "",
340 filter => "(".$rowldap[6]."=%s)" 366 filter => "(".$rowldap[6]."=%s)"
341 ); 367 );
342 my $method = $r->method; 368 my $method = $r->method;
343 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/)); 369 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
344 370
345 } 371 }
346 $sthldap->finish(); 372 $sthldap->finish();
347 undef $sthldap; 373 undef $sthldap;
348 } 374 }
352 $dbh->disconnect(); 378 $dbh->disconnect();
353 undef $dbh; 379 undef $dbh;
354 380
355 if ($cfg->{RedmineCacheCredsMax} and $ret) { 381 if ($cfg->{RedmineCacheCredsMax} and $ret) {
356 if (defined $usrprojpass) { 382 if (defined $usrprojpass) {
357 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest); 383 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
358 } else { 384 } else {
359 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) { 385 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
360 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest); 386 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
361 $cfg->{RedmineCacheCredsCount}++; 387 $cfg->{RedmineCacheCredsCount}++;
362 } else { 388 } else {
363 $cfg->{RedmineCacheCreds}->clear(); 389 $cfg->{RedmineCacheCreds}->clear();
364 $cfg->{RedmineCacheCredsCount} = 0; 390 $cfg->{RedmineCacheCredsCount} = 0;
365 } 391 }
369 $ret; 395 $ret;
370 } 396 }
371 397
372 sub get_project_identifier { 398 sub get_project_identifier {
373 my $r = shift; 399 my $r = shift;
374 400
375 my $location = $r->location; 401 my $location = $r->location;
376 my ($identifier) = $r->uri =~ m{$location/*([^/]+)}; 402 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
377 $identifier; 403 $identifier;
378 } 404 }
379 405
380 sub connect_database { 406 sub connect_database {
381 my $r = shift; 407 my $r = shift;
382 408
383 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); 409 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
384 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass}); 410 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
385 } 411 }
386 412
387 1; 413 1;