##// END OF EJS Templates
Merged r16286 (#24307)....
Jean-Philippe Lang -
r15914:b92d7879ff70
parent child
Show More
@@ -1,557 +1,560
1 1 package Apache::Authn::Redmine;
2 2
3 3 =head1 Apache::Authn::Redmine
4 4
5 5 Redmine - a mod_perl module to authenticate webdav subversion users
6 6 against redmine database
7 7
8 8 =head1 SYNOPSIS
9 9
10 10 This module allow anonymous users to browse public project and
11 11 registred users to browse and commit their project. Authentication is
12 12 done against the redmine database or the LDAP configured in redmine.
13 13
14 14 This method is far simpler than the one with pam_* and works with all
15 15 database without an hassle but you need to have apache/mod_perl on the
16 16 svn server.
17 17
18 18 =head1 INSTALLATION
19 19
20 20 For this to automagically work, you need to have a recent reposman.rb
21 21 (after r860) and if you already use reposman, read the last section to
22 22 migrate.
23 23
24 24 Sorry ruby users but you need some perl modules, at least mod_perl2,
25 25 DBI and DBD::mysql (or the DBD driver for you database as it should
26 26 work on allmost all databases).
27 27
28 28 On debian/ubuntu you must do :
29 29
30 30 aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl
31 31
32 32 If your Redmine users use LDAP authentication, you will also need
33 33 Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
34 34
35 35 aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
36 36
37 37 =head1 CONFIGURATION
38 38
39 39 ## This module has to be in your perl path
40 40 ## eg: /usr/lib/perl5/Apache/Authn/Redmine.pm
41 41 PerlLoadModule Apache::Authn::Redmine
42 42 <Location /svn>
43 43 DAV svn
44 44 SVNParentPath "/var/svn"
45 45
46 46 AuthType Basic
47 47 AuthName redmine
48 48 Require valid-user
49 49
50 50 PerlAccessHandler Apache::Authn::Redmine::access_handler
51 51 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
52 52
53 53 ## for mysql
54 54 RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
55 55 ## for postgres
56 56 # RedmineDSN "DBI:Pg:dbname=databasename;host=my.db.server"
57 57
58 58 RedmineDbUser "redmine"
59 59 RedmineDbPass "password"
60 60 ## Optional where clause (fulltext search would be slow and
61 61 ## database dependant).
62 62 # RedmineDbWhereClause "and members.role_id IN (1,2)"
63 63 ## Optional credentials cache size
64 64 # RedmineCacheCredsMax 50
65 65 </Location>
66 66
67 67 To be able to browse repository inside redmine, you must add something
68 68 like that :
69 69
70 70 <Location /svn-private>
71 71 DAV svn
72 72 SVNParentPath "/var/svn"
73 73 Order deny,allow
74 74 Deny from all
75 75 # only allow reading orders
76 76 <Limit GET PROPFIND OPTIONS REPORT>
77 77 Allow from redmine.server.ip
78 78 </Limit>
79 79 </Location>
80 80
81 81 and you will have to use this reposman.rb command line to create repository :
82 82
83 83 reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
84 84
85 85 =head1 REPOSITORIES NAMING
86 86
87 87 A projet repository must be named with the projet identifier. In case
88 88 of multiple repositories for the same project, use the project identifier
89 89 and the repository identifier separated with a dot:
90 90
91 91 /var/svn/foo
92 92 /var/svn/foo.otherrepo
93 93
94 94 =head1 MIGRATION FROM OLDER RELEASES
95 95
96 96 If you use an older reposman.rb (r860 or before), you need to change
97 97 rights on repositories to allow the apache user to read and write
98 98 S<them :>
99 99
100 100 sudo chown -R www-data /var/svn/*
101 101 sudo chmod -R u+w /var/svn/*
102 102
103 103 And you need to upgrade at least reposman.rb (after r860).
104 104
105 105 =head1 GIT SMART HTTP SUPPORT
106 106
107 107 Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
108 108 above settings. Redmine.pm normally does access control depending on the HTTP
109 109 method used: read-only methods are OK for everyone in public projects and
110 110 members with read rights in private projects. The rest require membership with
111 111 commit rights in the project.
112 112
113 113 However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
114 114 POST even for a simple clone. Instead, read-only requests must be detected using
115 115 the full URL (including the query string): anything that doesn't belong to the
116 116 git-receive-pack service is read-only.
117 117
118 118 To activate this mode of operation, add this line inside your <Location /git>
119 119 block:
120 120
121 121 RedmineGitSmartHttp yes
122 122
123 123 Here's a sample Apache configuration which integrates git-http-backend with
124 124 a MySQL database and this new option:
125 125
126 126 SetEnv GIT_PROJECT_ROOT /var/www/git/
127 127 SetEnv GIT_HTTP_EXPORT_ALL
128 128 ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
129 129 <Location /git>
130 130 Order allow,deny
131 131 Allow from all
132 132
133 133 AuthType Basic
134 134 AuthName Git
135 135 Require valid-user
136 136
137 137 PerlAccessHandler Apache::Authn::Redmine::access_handler
138 138 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
139 139 # for mysql
140 140 RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
141 141 RedmineDbUser "redmine"
142 142 RedmineDbPass "xxx"
143 143 RedmineGitSmartHttp yes
144 144 </Location>
145 145
146 146 Make sure that all the names of the repositories under /var/www/git/ have a
147 147 matching identifier for some project: /var/www/git/myproject and
148 148 /var/www/git/myproject.git will work. You can put both bare and non-bare
149 149 repositories in /var/www/git, though bare repositories are strongly
150 150 recommended. You should create them with the rights of the user running Redmine,
151 151 like this:
152 152
153 153 cd /var/www/git
154 154 sudo -u user-running-redmine mkdir myproject
155 155 cd myproject
156 156 sudo -u user-running-redmine git init --bare
157 157
158 158 Once you have activated this option, you have three options when cloning a
159 159 repository:
160 160
161 161 - Cloning using "http://user@host/git/repo(.git)" works, but will ask for the password
162 162 all the time.
163 163
164 164 - Cloning with "http://user:pass@host/git/repo(.git)" does not have this problem, but
165 165 this could reveal accidentally your password to the console in some versions
166 166 of Git, and you would have to ensure that .git/config is not readable except
167 167 by the owner for each of your projects.
168 168
169 169 - Use "http://host/git/repo(.git)", and store your credentials in the ~/.netrc
170 170 file. This is the recommended solution, as you only have one file to protect
171 171 and passwords will not be leaked accidentally to the console.
172 172
173 173 IMPORTANT NOTE: It is *very important* that the file cannot be read by other
174 174 users, as it will contain your password in cleartext. To create the file, you
175 175 can use the following commands, replacing yourhost, youruser and yourpassword
176 176 with the right values:
177 177
178 178 touch ~/.netrc
179 179 chmod 600 ~/.netrc
180 180 echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
181 181
182 182 =cut
183 183
184 184 use strict;
185 185 use warnings FATAL => 'all', NONFATAL => 'redefine';
186 186
187 187 use DBI;
188 188 use Digest::SHA;
189 189 # optional module for LDAP authentication
190 190 my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
191 191
192 192 use Apache2::Module;
193 193 use Apache2::Access;
194 194 use Apache2::ServerRec qw();
195 195 use Apache2::RequestRec qw();
196 196 use Apache2::RequestUtil qw();
197 197 use Apache2::Const qw(:common :override :cmd_how);
198 198 use APR::Pool ();
199 199 use APR::Table ();
200 200
201 201 # use Apache2::Directive qw();
202 202
203 203 my @directives = (
204 204 {
205 205 name => 'RedmineDSN',
206 206 req_override => OR_AUTHCFG,
207 207 args_how => TAKE1,
208 208 errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
209 209 },
210 210 {
211 211 name => 'RedmineDbUser',
212 212 req_override => OR_AUTHCFG,
213 213 args_how => TAKE1,
214 214 },
215 215 {
216 216 name => 'RedmineDbPass',
217 217 req_override => OR_AUTHCFG,
218 218 args_how => TAKE1,
219 219 },
220 220 {
221 221 name => 'RedmineDbWhereClause',
222 222 req_override => OR_AUTHCFG,
223 223 args_how => TAKE1,
224 224 },
225 225 {
226 226 name => 'RedmineCacheCredsMax',
227 227 req_override => OR_AUTHCFG,
228 228 args_how => TAKE1,
229 229 errmsg => 'RedmineCacheCredsMax must be decimal number',
230 230 },
231 231 {
232 232 name => 'RedmineGitSmartHttp',
233 233 req_override => OR_AUTHCFG,
234 234 args_how => TAKE1,
235 235 },
236 236 );
237 237
238 238 sub RedmineDSN {
239 239 my ($self, $parms, $arg) = @_;
240 240 $self->{RedmineDSN} = $arg;
241 241 my $query = "SELECT
242 242 users.hashed_password, users.salt, users.auth_source_id, roles.permissions, projects.status
243 243 FROM projects, users, roles
244 244 WHERE
245 245 users.login=?
246 246 AND projects.identifier=?
247 AND EXISTS (SELECT 1 FROM enabled_modules em WHERE em.project_id = projects.id AND em.name = 'repository')
247 248 AND users.type='User'
248 249 AND users.status=1
249 250 AND (
250 251 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)
251 252 OR
252 253 (cast(projects.is_public as CHAR) IN ('t', '1')
253 254 AND (roles.builtin=1
254 255 OR roles.id IN (SELECT member_roles.role_id FROM members, member_roles, users g
255 256 WHERE members.user_id = g.id AND members.project_id = projects.id AND members.id = member_roles.member_id
256 257 AND g.type = 'GroupNonMember'))
257 258 )
258 259 )
259 260 AND roles.permissions IS NOT NULL";
260 261 $self->{RedmineQuery} = trim($query);
261 262 }
262 263
263 264 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
264 265 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
265 266 sub RedmineDbWhereClause {
266 267 my ($self, $parms, $arg) = @_;
267 268 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
268 269 }
269 270
270 271 sub RedmineCacheCredsMax {
271 272 my ($self, $parms, $arg) = @_;
272 273 if ($arg) {
273 274 $self->{RedmineCachePool} = APR::Pool->new;
274 275 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
275 276 $self->{RedmineCacheCredsCount} = 0;
276 277 $self->{RedmineCacheCredsMax} = $arg;
277 278 }
278 279 }
279 280
280 281 sub RedmineGitSmartHttp {
281 282 my ($self, $parms, $arg) = @_;
282 283 $arg = lc $arg;
283 284
284 285 if ($arg eq "yes" || $arg eq "true") {
285 286 $self->{RedmineGitSmartHttp} = 1;
286 287 } else {
287 288 $self->{RedmineGitSmartHttp} = 0;
288 289 }
289 290 }
290 291
291 292 sub trim {
292 293 my $string = shift;
293 294 $string =~ s/\s{2,}/ /g;
294 295 return $string;
295 296 }
296 297
297 298 sub set_val {
298 299 my ($key, $self, $parms, $arg) = @_;
299 300 $self->{$key} = $arg;
300 301 }
301 302
302 303 Apache2::Module::add(__PACKAGE__, \@directives);
303 304
304 305
305 306 my %read_only_methods = map { $_ => 1 } qw/GET HEAD PROPFIND REPORT OPTIONS/;
306 307
307 308 sub request_is_read_only {
308 309 my ($r) = @_;
309 310 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
310 311
311 312 # Do we use Git's smart HTTP protocol, or not?
312 313 if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
313 314 my $uri = $r->unparsed_uri;
314 315 my $location = $r->location;
315 316 my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
316 317 return $is_read_only;
317 318 } else {
318 319 # Standard behaviour: check the HTTP method
319 320 my $method = $r->method;
320 321 return defined $read_only_methods{$method};
321 322 }
322 323 }
323 324
324 325 sub access_handler {
325 326 my $r = shift;
326 327
327 328 unless ($r->some_auth_required) {
328 329 $r->log_reason("No authentication has been configured");
329 330 return FORBIDDEN;
330 331 }
331 332
332 333 return OK unless request_is_read_only($r);
333 334
334 335 my $project_id = get_project_identifier($r);
335 336
336 337 if (is_public_project($project_id, $r) && anonymous_allowed_to_browse_repository($project_id, $r)) {
337 338 $r->user("");
338 339 $r->set_handlers(PerlAuthenHandler => [\&OK]);
339 340 }
340 341
341 342 return OK
342 343 }
343 344
344 345 sub authen_handler {
345 346 my $r = shift;
346 347
347 348 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
348 349 return $res unless $res == OK;
349 350
350 351 if (is_member($r->user, $redmine_pass, $r)) {
351 352 return OK;
352 353 } else {
353 354 $r->note_auth_failure();
354 355 return DECLINED;
355 356 }
356 357 }
357 358
358 359 # check if authentication is forced
359 360 sub is_authentication_forced {
360 361 my $r = shift;
361 362
362 363 my $dbh = connect_database($r);
363 364 my $sth = $dbh->prepare(
364 365 "SELECT value FROM settings where settings.name = 'login_required';"
365 366 );
366 367
367 368 $sth->execute();
368 369 my $ret = 0;
369 370 if (my @row = $sth->fetchrow_array) {
370 371 if ($row[0] eq "1" || $row[0] eq "t") {
371 372 $ret = 1;
372 373 }
373 374 }
374 375 $sth->finish();
375 376 undef $sth;
376 377
377 378 $dbh->disconnect();
378 379 undef $dbh;
379 380
380 381 $ret;
381 382 }
382 383
383 384 sub is_public_project {
384 385 my $project_id = shift;
385 386 my $r = shift;
386 387
387 388 if (is_authentication_forced($r)) {
388 389 return 0;
389 390 }
390 391
391 392 my $dbh = connect_database($r);
392 393 my $sth = $dbh->prepare(
393 "SELECT is_public FROM projects WHERE projects.identifier = ? AND projects.status <> 9;"
394 "SELECT is_public FROM projects
395 WHERE projects.identifier = ? AND projects.status <> 9
396 AND EXISTS (SELECT 1 FROM enabled_modules em WHERE em.project_id = projects.id AND em.name = 'repository');"
394 397 );
395 398
396 399 $sth->execute($project_id);
397 400 my $ret = 0;
398 401 if (my @row = $sth->fetchrow_array) {
399 402 if ($row[0] eq "1" || $row[0] eq "t") {
400 403 $ret = 1;
401 404 }
402 405 }
403 406 $sth->finish();
404 407 undef $sth;
405 408 $dbh->disconnect();
406 409 undef $dbh;
407 410
408 411 $ret;
409 412 }
410 413
411 414 sub anonymous_allowed_to_browse_repository {
412 415 my $project_id = shift;
413 416 my $r = shift;
414 417
415 418 my $dbh = connect_database($r);
416 419 my $sth = $dbh->prepare(
417 420 "SELECT permissions FROM roles WHERE permissions like '%browse_repository%'
418 421 AND (roles.builtin = 2
419 422 OR roles.id IN (SELECT member_roles.role_id FROM projects, members, member_roles, users
420 423 WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id
421 424 AND projects.identifier = ? AND users.type = 'GroupAnonymous'));"
422 425 );
423 426
424 427 $sth->execute($project_id);
425 428 my $ret = 0;
426 429 if (my @row = $sth->fetchrow_array) {
427 430 if ($row[0] =~ /:browse_repository/) {
428 431 $ret = 1;
429 432 }
430 433 }
431 434 $sth->finish();
432 435 undef $sth;
433 436 $dbh->disconnect();
434 437 undef $dbh;
435 438
436 439 $ret;
437 440 }
438 441
439 442 # perhaps we should use repository right (other read right) to check public access.
440 443 # it could be faster BUT it doesn't work for the moment.
441 444 # sub is_public_project_by_file {
442 445 # my $project_id = shift;
443 446 # my $r = shift;
444 447
445 448 # my $tree = Apache2::Directive::conftree();
446 449 # my $node = $tree->lookup('Location', $r->location);
447 450 # my $hash = $node->as_hash;
448 451
449 452 # my $svnparentpath = $hash->{SVNParentPath};
450 453 # my $repos_path = $svnparentpath . "/" . $project_id;
451 454 # return 1 if (stat($repos_path))[2] & 00007;
452 455 # }
453 456
454 457 sub is_member {
455 458 my $redmine_user = shift;
456 459 my $redmine_pass = shift;
457 460 my $r = shift;
458 461
459 462 my $project_id = get_project_identifier($r);
460 463
461 464 my $pass_digest = Digest::SHA::sha1_hex($redmine_pass);
462 465
463 466 my $access_mode = request_is_read_only($r) ? "R" : "W";
464 467
465 468 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
466 469 my $usrprojpass;
467 470 if ($cfg->{RedmineCacheCredsMax}) {
468 471 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
469 472 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
470 473 }
471 474 my $dbh = connect_database($r);
472 475 my $query = $cfg->{RedmineQuery};
473 476 my $sth = $dbh->prepare($query);
474 477 $sth->execute($redmine_user, $project_id);
475 478
476 479 my $ret;
477 480 while (my ($hashed_password, $salt, $auth_source_id, $permissions, $project_status) = $sth->fetchrow_array) {
478 481 if ($project_status eq "9" || ($project_status ne "1" && $access_mode eq "W")) {
479 482 last;
480 483 }
481 484
482 485 unless ($auth_source_id) {
483 486 my $method = $r->method;
484 487 my $salted_password = Digest::SHA::sha1_hex($salt.$pass_digest);
485 488 if ($hashed_password eq $salted_password && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
486 489 $ret = 1;
487 490 last;
488 491 }
489 492 } elsif ($CanUseLDAPAuth) {
490 493 my $sthldap = $dbh->prepare(
491 494 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
492 495 );
493 496 $sthldap->execute($auth_source_id);
494 497 while (my @rowldap = $sthldap->fetchrow_array) {
495 498 my $bind_as = $rowldap[3] ? $rowldap[3] : "";
496 499 my $bind_pw = $rowldap[4] ? $rowldap[4] : "";
497 500 if ($bind_as =~ m/\$login/) {
498 501 # replace $login with $redmine_user and use $redmine_pass
499 502 $bind_as =~ s/\$login/$redmine_user/g;
500 503 $bind_pw = $redmine_pass
501 504 }
502 505 my $ldap = Authen::Simple::LDAP->new(
503 506 host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]:$rowldap[1]" : $rowldap[0],
504 507 port => $rowldap[1],
505 508 basedn => $rowldap[5],
506 509 binddn => $bind_as,
507 510 bindpw => $bind_pw,
508 511 filter => "(".$rowldap[6]."=%s)"
509 512 );
510 513 my $method = $r->method;
511 514 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
512 515
513 516 }
514 517 $sthldap->finish();
515 518 undef $sthldap;
516 519 }
517 520 }
518 521 $sth->finish();
519 522 undef $sth;
520 523 $dbh->disconnect();
521 524 undef $dbh;
522 525
523 526 if ($cfg->{RedmineCacheCredsMax} and $ret) {
524 527 if (defined $usrprojpass) {
525 528 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
526 529 } else {
527 530 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
528 531 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
529 532 $cfg->{RedmineCacheCredsCount}++;
530 533 } else {
531 534 $cfg->{RedmineCacheCreds}->clear();
532 535 $cfg->{RedmineCacheCredsCount} = 0;
533 536 }
534 537 }
535 538 }
536 539
537 540 $ret;
538 541 }
539 542
540 543 sub get_project_identifier {
541 544 my $r = shift;
542 545
543 546 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
544 547 my $location = $r->location;
545 548 $location =~ s/\.git$// if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp});
546 549 my ($identifier) = $r->uri =~ m{$location/*([^/.]+)};
547 550 $identifier;
548 551 }
549 552
550 553 sub connect_database {
551 554 my $r = shift;
552 555
553 556 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
554 557 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
555 558 }
556 559
557 560 1;
@@ -1,309 +1,332
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../test_case', __FILE__)
19 19 require 'tmpdir'
20 20
21 21 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase
22 fixtures :projects, :users, :members, :roles, :member_roles, :auth_sources
22 fixtures :projects, :users, :members, :roles, :member_roles, :auth_sources, :enabled_modules
23 23
24 24 SVN_BIN = Redmine::Configuration['scm_subversion_command'] || "svn"
25 25
26 26 def test_anonymous_read_on_public_repo_with_permission_should_succeed
27 27 assert_success "ls", svn_url
28 28 end
29 29
30 30 def test_anonymous_read_on_public_repo_with_anonymous_group_permission_should_succeed
31 31 Role.anonymous.remove_permission! :browse_repository
32 32 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
33 33 assert_success "ls", svn_url
34 34 end
35 35
36 36 def test_anonymous_read_on_public_repo_without_permission_should_fail
37 37 Role.anonymous.remove_permission! :browse_repository
38 38 assert_failure "ls", svn_url
39 39 end
40 40
41 def test_anonymous_read_on_public_project_with_module_disabled_should_fail
42 Project.find(1).disable_module! :repository
43 assert_failure "ls", svn_url
44 end
45
41 46 def test_anonymous_read_on_private_repo_should_fail
42 47 Project.find(1).update_attribute :is_public, false
43 48 assert_failure "ls", svn_url
44 49 end
45 50
46 51 def test_anonymous_commit_on_public_repo_should_fail
47 52 Role.anonymous.add_permission! :commit_access
48 53 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
49 54 end
50 55
51 56 def test_anonymous_commit_on_private_repo_should_fail
52 57 Role.anonymous.add_permission! :commit_access
53 58 Project.find(1).update_attribute :is_public, false
54 59 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
55 60 end
56 61
57 62 def test_non_member_read_on_public_repo_with_permission_should_succeed
58 63 Role.anonymous.remove_permission! :browse_repository
59 64 with_credentials "miscuser8", "foo" do
60 65 assert_success "ls", svn_url
61 66 end
62 67 end
63 68
64 69 def test_non_member_read_on_public_repo_with_non_member_group_permission_should_succeed
65 70 Role.anonymous.remove_permission! :browse_repository
66 71 Role.non_member.remove_permission! :browse_repository
67 72 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
68 73 with_credentials "miscuser8", "foo" do
69 74 assert_success "ls", svn_url
70 75 end
71 76 end
72 77
73 78 def test_non_member_read_on_public_repo_without_permission_should_fail
74 79 Role.anonymous.remove_permission! :browse_repository
75 80 Role.non_member.remove_permission! :browse_repository
76 81 with_credentials "miscuser8", "foo" do
77 82 assert_failure "ls", svn_url
78 83 end
79 84 end
80 85
81 86 def test_non_member_read_on_private_repo_should_fail
82 87 Project.find(1).update_attribute :is_public, false
83 88 with_credentials "miscuser8", "foo" do
84 89 assert_failure "ls", svn_url
85 90 end
86 91 end
87 92
88 93 def test_non_member_commit_on_public_repo_should_fail
89 94 Role.non_member.add_permission! :commit_access
90 95 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
91 96 end
92 97
93 98 def test_non_member_commit_on_private_repo_should_fail
94 99 Role.non_member.add_permission! :commit_access
95 100 Project.find(1).update_attribute :is_public, false
96 101 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
97 102 end
98 103
99 104 def test_member_read_on_public_repo_with_permission_should_succeed
100 105 Role.anonymous.remove_permission! :browse_repository
101 106 Role.non_member.remove_permission! :browse_repository
102 107 with_credentials "dlopper", "foo" do
103 108 assert_success "ls", svn_url
104 109 end
105 110 end
106 111
107 112 def test_member_read_on_public_repo_without_permission_should_fail
108 113 Role.anonymous.remove_permission! :browse_repository
109 114 Role.non_member.remove_permission! :browse_repository
110 115 Role.find(2).remove_permission! :browse_repository
111 116 with_credentials "dlopper", "foo" do
112 117 assert_failure "ls", svn_url
113 118 end
114 119 end
115 120
116 121 def test_member_read_on_private_repo_with_permission_should_succeed
117 122 Project.find(1).update_attribute :is_public, false
118 123 with_credentials "dlopper", "foo" do
119 124 assert_success "ls", svn_url
120 125 end
121 126 end
122 127
123 128 def test_member_read_on_private_repo_without_permission_should_fail
124 129 Role.find(2).remove_permission! :browse_repository
125 130 Project.find(1).update_attribute :is_public, false
126 131 with_credentials "dlopper", "foo" do
127 132 assert_failure "ls", svn_url
128 133 end
129 134 end
130 135
136 def test_member_read_on_private_repo_with_module_disabled_should_fail
137 Role.find(2).add_permission! :browse_repository
138 Project.find(1).update_attribute :is_public, false
139 Project.find(1).disable_module! :repository
140 with_credentials "dlopper", "foo" do
141 assert_failure "ls", svn_url
142 end
143 end
144
131 145 def test_member_commit_on_public_repo_with_permission_should_succeed
132 146 Role.find(2).add_permission! :commit_access
133 147 with_credentials "dlopper", "foo" do
134 148 assert_success "mkdir --message Creating_a_directory", svn_url(random_filename)
135 149 end
136 150 end
137 151
138 152 def test_member_commit_on_public_repo_without_permission_should_fail
139 153 Role.find(2).remove_permission! :commit_access
140 154 with_credentials "dlopper", "foo" do
141 155 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
142 156 end
143 157 end
144 158
145 159 def test_member_commit_on_private_repo_with_permission_should_succeed
146 160 Role.find(2).add_permission! :commit_access
147 161 Project.find(1).update_attribute :is_public, false
148 162 with_credentials "dlopper", "foo" do
149 163 assert_success "mkdir --message Creating_a_directory", svn_url(random_filename)
150 164 end
151 165 end
152 166
153 167 def test_member_commit_on_private_repo_without_permission_should_fail
154 168 Role.find(2).remove_permission! :commit_access
155 169 Project.find(1).update_attribute :is_public, false
156 170 with_credentials "dlopper", "foo" do
157 171 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
158 172 end
159 173 end
160 174
175 def test_member_commit_on_private_repo_with_module_disabled_should_fail
176 Role.find(2).add_permission! :commit_access
177 Project.find(1).update_attribute :is_public, false
178 Project.find(1).disable_module! :repository
179 with_credentials "dlopper", "foo" do
180 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
181 end
182 end
183
161 184 def test_invalid_credentials_should_fail
162 185 Project.find(1).update_attribute :is_public, false
163 186 with_credentials "dlopper", "foo" do
164 187 assert_success "ls", svn_url
165 188 end
166 189 with_credentials "dlopper", "wrong" do
167 190 assert_failure "ls", svn_url
168 191 end
169 192 end
170 193
171 194 def test_anonymous_read_should_fail_with_login_required
172 195 assert_success "ls", svn_url
173 196 with_settings :login_required => '1' do
174 197 assert_failure "ls", svn_url
175 198 end
176 199 end
177 200
178 201 def test_authenticated_read_should_succeed_with_login_required
179 202 with_settings :login_required => '1' do
180 203 with_credentials "miscuser8", "foo" do
181 204 assert_success "ls", svn_url
182 205 end
183 206 end
184 207 end
185 208
186 209 def test_read_on_archived_projects_should_fail
187 210 Project.find(1).update_attribute :status, Project::STATUS_ARCHIVED
188 211 assert_failure "ls", svn_url
189 212 end
190 213
191 214 def test_read_on_archived_private_projects_should_fail
192 215 Project.find(1).update_attribute :status, Project::STATUS_ARCHIVED
193 216 Project.find(1).update_attribute :is_public, false
194 217 with_credentials "dlopper", "foo" do
195 218 assert_failure "ls", svn_url
196 219 end
197 220 end
198 221
199 222 def test_read_on_closed_projects_should_succeed
200 223 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
201 224 assert_success "ls", svn_url
202 225 end
203 226
204 227 def test_read_on_closed_private_projects_should_succeed
205 228 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
206 229 Project.find(1).update_attribute :is_public, false
207 230 with_credentials "dlopper", "foo" do
208 231 assert_success "ls", svn_url
209 232 end
210 233 end
211 234
212 235 def test_commit_on_closed_projects_should_fail
213 236 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
214 237 Role.find(2).add_permission! :commit_access
215 238 with_credentials "dlopper", "foo" do
216 239 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
217 240 end
218 241 end
219 242
220 243 def test_commit_on_closed_private_projects_should_fail
221 244 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
222 245 Project.find(1).update_attribute :is_public, false
223 246 Role.find(2).add_permission! :commit_access
224 247 with_credentials "dlopper", "foo" do
225 248 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
226 249 end
227 250 end
228 251
229 252 if ldap_configured?
230 253 def test_user_with_ldap_auth_source_should_authenticate_with_ldap_credentials
231 254 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
232 255 ldap_user.login = 'example1'
233 256 ldap_user.save!
234 257
235 258 with_settings :login_required => '1' do
236 259 with_credentials "example1", "123456" do
237 260 assert_success "ls", svn_url
238 261 end
239 262 end
240 263
241 264 with_settings :login_required => '1' do
242 265 with_credentials "example1", "wrong" do
243 266 assert_failure "ls", svn_url
244 267 end
245 268 end
246 269 end
247 270 end
248 271
249 272 def test_checkout
250 273 Dir.mktmpdir do |dir|
251 274 assert_success "checkout", svn_url, dir
252 275 end
253 276 end
254 277
255 278 def test_read_commands
256 279 assert_success "info", svn_url
257 280 assert_success "ls", svn_url
258 281 assert_success "log", svn_url
259 282 end
260 283
261 284 def test_write_commands
262 285 Role.find(2).add_permission! :commit_access
263 286 filename = random_filename
264 287
265 288 Dir.mktmpdir do |dir|
266 289 assert_success "checkout", svn_url, dir
267 290 Dir.chdir(dir) do
268 291 # creates a file in the working copy
269 292 f = File.new(File.join(dir, filename), "w")
270 293 f.write "test file content"
271 294 f.close
272 295
273 296 assert_success "add", filename
274 297 with_credentials "dlopper", "foo" do
275 298 assert_success "commit --message Committing_a_file"
276 299 assert_success "copy --message Copying_a_file", svn_url(filename), svn_url("#{filename}_copy")
277 300 assert_success "delete --message Deleting_a_file", svn_url(filename)
278 301 assert_success "mkdir --message Creating_a_directory", svn_url("#{filename}_dir")
279 302 end
280 303 assert_success "update"
281 304
282 305 # checks that the working copy was updated
283 306 assert File.exists?(File.join(dir, "#{filename}_copy"))
284 307 assert File.directory?(File.join(dir, "#{filename}_dir"))
285 308 end
286 309 end
287 310 end
288 311
289 312 def test_read_invalid_repo_should_fail
290 313 assert_failure "ls", svn_url("invalid")
291 314 end
292 315
293 316 protected
294 317
295 318 def execute(*args)
296 319 a = [SVN_BIN, "--no-auth-cache --non-interactive"]
297 320 a << "--username #{username}" if username
298 321 a << "--password #{password}" if password
299 322
300 323 super a, *args
301 324 end
302 325
303 326 def svn_url(path=nil)
304 327 host = ENV['REDMINE_TEST_DAV_SERVER'] || '127.0.0.1'
305 328 url = "http://#{host}/svn/ecookbook"
306 329 url << "/#{path}" if path
307 330 url
308 331 end
309 332 end
General Comments 0
You need to be logged in to leave comments. Login now