##// END OF EJS Templates
Makes Redmine.pm handle project status (#3640)....
Jean-Philippe Lang -
r9704:21ac4a3d0e07
parent child
Show More
@@ -1,540 +1,543
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 hashed_password, salt, auth_source_id, permissions
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 247 AND users.status=1
248 248 AND (
249 249 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)
250 250 OR
251 251 (roles.builtin=1 AND cast(projects.is_public as CHAR) IN ('t', '1'))
252 252 ) ";
253 253 $self->{RedmineQuery} = trim($query);
254 254 }
255 255
256 256 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
257 257 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
258 258 sub RedmineDbWhereClause {
259 259 my ($self, $parms, $arg) = @_;
260 260 $self->{RedmineQuery} = trim($self->{RedmineQuery}.($arg ? $arg : "")." ");
261 261 }
262 262
263 263 sub RedmineCacheCredsMax {
264 264 my ($self, $parms, $arg) = @_;
265 265 if ($arg) {
266 266 $self->{RedmineCachePool} = APR::Pool->new;
267 267 $self->{RedmineCacheCreds} = APR::Table::make($self->{RedmineCachePool}, $arg);
268 268 $self->{RedmineCacheCredsCount} = 0;
269 269 $self->{RedmineCacheCredsMax} = $arg;
270 270 }
271 271 }
272 272
273 273 sub RedmineGitSmartHttp {
274 274 my ($self, $parms, $arg) = @_;
275 275 $arg = lc $arg;
276 276
277 277 if ($arg eq "yes" || $arg eq "true") {
278 278 $self->{RedmineGitSmartHttp} = 1;
279 279 } else {
280 280 $self->{RedmineGitSmartHttp} = 0;
281 281 }
282 282 }
283 283
284 284 sub trim {
285 285 my $string = shift;
286 286 $string =~ s/\s{2,}/ /g;
287 287 return $string;
288 288 }
289 289
290 290 sub set_val {
291 291 my ($key, $self, $parms, $arg) = @_;
292 292 $self->{$key} = $arg;
293 293 }
294 294
295 295 Apache2::Module::add(__PACKAGE__, \@directives);
296 296
297 297
298 298 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
299 299
300 300 sub request_is_read_only {
301 301 my ($r) = @_;
302 302 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
303 303
304 304 # Do we use Git's smart HTTP protocol, or not?
305 305 if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
306 306 my $uri = $r->unparsed_uri;
307 307 my $location = $r->location;
308 308 my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
309 309 return $is_read_only;
310 310 } else {
311 311 # Standard behaviour: check the HTTP method
312 312 my $method = $r->method;
313 313 return defined $read_only_methods{$method};
314 314 }
315 315 }
316 316
317 317 sub access_handler {
318 318 my $r = shift;
319 319
320 320 unless ($r->some_auth_required) {
321 321 $r->log_reason("No authentication has been configured");
322 322 return FORBIDDEN;
323 323 }
324 324
325 325 return OK unless request_is_read_only($r);
326 326
327 327 my $project_id = get_project_identifier($r);
328 328
329 329 $r->set_handlers(PerlAuthenHandler => [\&OK])
330 330 if is_public_project($project_id, $r) && anonymous_role_allows_browse_repository($r);
331 331
332 332 return OK
333 333 }
334 334
335 335 sub authen_handler {
336 336 my $r = shift;
337 337
338 338 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
339 339 return $res unless $res == OK;
340 340
341 341 if (is_member($r->user, $redmine_pass, $r)) {
342 342 return OK;
343 343 } else {
344 344 $r->note_auth_failure();
345 345 return AUTH_REQUIRED;
346 346 }
347 347 }
348 348
349 349 # check if authentication is forced
350 350 sub is_authentication_forced {
351 351 my $r = shift;
352 352
353 353 my $dbh = connect_database($r);
354 354 my $sth = $dbh->prepare(
355 355 "SELECT value FROM settings where settings.name = 'login_required';"
356 356 );
357 357
358 358 $sth->execute();
359 359 my $ret = 0;
360 360 if (my @row = $sth->fetchrow_array) {
361 361 if ($row[0] eq "1" || $row[0] eq "t") {
362 362 $ret = 1;
363 363 }
364 364 }
365 365 $sth->finish();
366 366 undef $sth;
367 367
368 368 $dbh->disconnect();
369 369 undef $dbh;
370 370
371 371 $ret;
372 372 }
373 373
374 374 sub is_public_project {
375 375 my $project_id = shift;
376 376 my $r = shift;
377 377
378 378 if (is_authentication_forced($r)) {
379 379 return 0;
380 380 }
381 381
382 382 my $dbh = connect_database($r);
383 383 my $sth = $dbh->prepare(
384 "SELECT is_public FROM projects WHERE projects.identifier = ?;"
384 "SELECT is_public FROM projects WHERE projects.identifier = ? AND projects.status <> 9;"
385 385 );
386 386
387 387 $sth->execute($project_id);
388 388 my $ret = 0;
389 389 if (my @row = $sth->fetchrow_array) {
390 390 if ($row[0] eq "1" || $row[0] eq "t") {
391 391 $ret = 1;
392 392 }
393 393 }
394 394 $sth->finish();
395 395 undef $sth;
396 396 $dbh->disconnect();
397 397 undef $dbh;
398 398
399 399 $ret;
400 400 }
401 401
402 402 sub anonymous_role_allows_browse_repository {
403 403 my $r = shift;
404 404
405 405 my $dbh = connect_database($r);
406 406 my $sth = $dbh->prepare(
407 407 "SELECT permissions FROM roles WHERE builtin = 2;"
408 408 );
409 409
410 410 $sth->execute();
411 411 my $ret = 0;
412 412 if (my @row = $sth->fetchrow_array) {
413 413 if ($row[0] =~ /:browse_repository/) {
414 414 $ret = 1;
415 415 }
416 416 }
417 417 $sth->finish();
418 418 undef $sth;
419 419 $dbh->disconnect();
420 420 undef $dbh;
421 421
422 422 $ret;
423 423 }
424 424
425 425 # perhaps we should use repository right (other read right) to check public access.
426 426 # it could be faster BUT it doesn't work for the moment.
427 427 # sub is_public_project_by_file {
428 428 # my $project_id = shift;
429 429 # my $r = shift;
430 430
431 431 # my $tree = Apache2::Directive::conftree();
432 432 # my $node = $tree->lookup('Location', $r->location);
433 433 # my $hash = $node->as_hash;
434 434
435 435 # my $svnparentpath = $hash->{SVNParentPath};
436 436 # my $repos_path = $svnparentpath . "/" . $project_id;
437 437 # return 1 if (stat($repos_path))[2] & 00007;
438 438 # }
439 439
440 440 sub is_member {
441 441 my $redmine_user = shift;
442 442 my $redmine_pass = shift;
443 443 my $r = shift;
444 444
445 445 my $dbh = connect_database($r);
446 446 my $project_id = get_project_identifier($r);
447 447
448 448 my $pass_digest = Digest::SHA::sha1_hex($redmine_pass);
449 449
450 450 my $access_mode = request_is_read_only($r) ? "R" : "W";
451 451
452 452 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
453 453 my $usrprojpass;
454 454 if ($cfg->{RedmineCacheCredsMax}) {
455 455 $usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
456 456 return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
457 457 }
458 458 my $query = $cfg->{RedmineQuery};
459 459 my $sth = $dbh->prepare($query);
460 460 $sth->execute($redmine_user, $project_id);
461 461
462 462 my $ret;
463 while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
463 while (my ($hashed_password, $salt, $auth_source_id, $permissions, $project_status) = $sth->fetchrow_array) {
464 if ($project_status eq "9" || ($project_status ne "1" && $access_mode eq "W")) {
465 last;
466 }
464 467
465 468 unless ($auth_source_id) {
466 469 my $method = $r->method;
467 470 my $salted_password = Digest::SHA::sha1_hex($salt.$pass_digest);
468 471 if ($hashed_password eq $salted_password && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
469 472 $ret = 1;
470 473 last;
471 474 }
472 475 } elsif ($CanUseLDAPAuth) {
473 476 my $sthldap = $dbh->prepare(
474 477 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
475 478 );
476 479 $sthldap->execute($auth_source_id);
477 480 while (my @rowldap = $sthldap->fetchrow_array) {
478 481 my $bind_as = $rowldap[3] ? $rowldap[3] : "";
479 482 my $bind_pw = $rowldap[4] ? $rowldap[4] : "";
480 483 if ($bind_as =~ m/\$login/) {
481 484 # replace $login with $redmine_user and use $redmine_pass
482 485 $bind_as =~ s/\$login/$redmine_user/g;
483 486 $bind_pw = $redmine_pass
484 487 }
485 488 my $ldap = Authen::Simple::LDAP->new(
486 489 host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]:$rowldap[1]" : $rowldap[0],
487 490 port => $rowldap[1],
488 491 basedn => $rowldap[5],
489 492 binddn => $bind_as,
490 493 bindpw => $bind_pw,
491 494 filter => "(".$rowldap[6]."=%s)"
492 495 );
493 496 my $method = $r->method;
494 497 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
495 498
496 499 }
497 500 $sthldap->finish();
498 501 undef $sthldap;
499 502 }
500 503 }
501 504 $sth->finish();
502 505 undef $sth;
503 506 $dbh->disconnect();
504 507 undef $dbh;
505 508
506 509 if ($cfg->{RedmineCacheCredsMax} and $ret) {
507 510 if (defined $usrprojpass) {
508 511 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
509 512 } else {
510 513 if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
511 514 $cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
512 515 $cfg->{RedmineCacheCredsCount}++;
513 516 } else {
514 517 $cfg->{RedmineCacheCreds}->clear();
515 518 $cfg->{RedmineCacheCredsCount} = 0;
516 519 }
517 520 }
518 521 }
519 522
520 523 $ret;
521 524 }
522 525
523 526 sub get_project_identifier {
524 527 my $r = shift;
525 528
526 529 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
527 530 my $location = $r->location;
528 531 $location =~ s/\.git$// if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp});
529 532 my ($identifier) = $r->uri =~ m{$location/*([^/.]+)};
530 533 $identifier;
531 534 }
532 535
533 536 sub connect_database {
534 537 my $r = shift;
535 538
536 539 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
537 540 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
538 541 }
539 542
540 543 1;
@@ -1,251 +1,294
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 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 22 fixtures :projects, :users, :members, :roles, :member_roles, :auth_sources
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_without_permission_should_fail
31 31 Role.anonymous.remove_permission! :browse_repository
32 32 assert_failure "ls", svn_url
33 33 end
34 34
35 35 def test_anonymous_read_on_private_repo_should_fail
36 36 Project.find(1).update_attribute :is_public, false
37 37 assert_failure "ls", svn_url
38 38 end
39 39
40 40 def test_anonymous_commit_on_public_repo_should_fail
41 41 Role.anonymous.add_permission! :commit_access
42 42 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
43 43 end
44 44
45 45 def test_anonymous_commit_on_private_repo_should_fail
46 46 Role.anonymous.add_permission! :commit_access
47 47 Project.find(1).update_attribute :is_public, false
48 48 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
49 49 end
50 50
51 51 def test_non_member_read_on_public_repo_with_permission_should_succeed
52 52 Role.anonymous.remove_permission! :browse_repository
53 53 with_credentials "miscuser8", "foo" do
54 54 assert_success "ls", svn_url
55 55 end
56 56 end
57 57
58 58 def test_non_member_read_on_public_repo_without_permission_should_fail
59 59 Role.anonymous.remove_permission! :browse_repository
60 60 Role.non_member.remove_permission! :browse_repository
61 61 with_credentials "miscuser8", "foo" do
62 62 assert_failure "ls", svn_url
63 63 end
64 64 end
65 65
66 66 def test_non_member_read_on_private_repo_should_fail
67 67 Project.find(1).update_attribute :is_public, false
68 68 with_credentials "miscuser8", "foo" do
69 69 assert_failure "ls", svn_url
70 70 end
71 71 end
72 72
73 73 def test_non_member_commit_on_public_repo_should_fail
74 74 Role.non_member.add_permission! :commit_access
75 75 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
76 76 end
77 77
78 78 def test_non_member_commit_on_private_repo_should_fail
79 79 Role.non_member.add_permission! :commit_access
80 80 Project.find(1).update_attribute :is_public, false
81 81 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
82 82 end
83 83
84 84 def test_member_read_on_public_repo_with_permission_should_succeed
85 85 Role.anonymous.remove_permission! :browse_repository
86 86 Role.non_member.remove_permission! :browse_repository
87 87 with_credentials "dlopper", "foo" do
88 88 assert_success "ls", svn_url
89 89 end
90 90 end
91 91
92 92 def test_member_read_on_public_repo_without_permission_should_fail
93 93 Role.anonymous.remove_permission! :browse_repository
94 94 Role.non_member.remove_permission! :browse_repository
95 95 Role.find(2).remove_permission! :browse_repository
96 96 with_credentials "dlopper", "foo" do
97 97 assert_failure "ls", svn_url
98 98 end
99 99 end
100 100
101 101 def test_member_read_on_private_repo_with_permission_should_succeed
102 102 Project.find(1).update_attribute :is_public, false
103 103 with_credentials "dlopper", "foo" do
104 104 assert_success "ls", svn_url
105 105 end
106 106 end
107 107
108 108 def test_member_read_on_private_repo_without_permission_should_fail
109 109 Role.find(2).remove_permission! :browse_repository
110 110 Project.find(1).update_attribute :is_public, false
111 111 with_credentials "dlopper", "foo" do
112 112 assert_failure "ls", svn_url
113 113 end
114 114 end
115 115
116 116 def test_member_commit_on_public_repo_with_permission_should_succeed
117 117 Role.find(2).add_permission! :commit_access
118 118 with_credentials "dlopper", "foo" do
119 119 assert_success "mkdir --message Creating_a_directory", svn_url(random_filename)
120 120 end
121 121 end
122 122
123 123 def test_member_commit_on_public_repo_without_permission_should_fail
124 124 Role.find(2).remove_permission! :commit_access
125 125 with_credentials "dlopper", "foo" do
126 126 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
127 127 end
128 128 end
129 129
130 130 def test_member_commit_on_private_repo_with_permission_should_succeed
131 131 Role.find(2).add_permission! :commit_access
132 132 Project.find(1).update_attribute :is_public, false
133 133 with_credentials "dlopper", "foo" do
134 134 assert_success "mkdir --message Creating_a_directory", svn_url(random_filename)
135 135 end
136 136 end
137 137
138 138 def test_member_commit_on_private_repo_without_permission_should_fail
139 139 Role.find(2).remove_permission! :commit_access
140 140 Project.find(1).update_attribute :is_public, false
141 141 with_credentials "dlopper", "foo" do
142 142 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
143 143 end
144 144 end
145 145
146 146 def test_invalid_credentials_should_fail
147 147 Project.find(1).update_attribute :is_public, false
148 148 with_credentials "dlopper", "foo" do
149 149 assert_success "ls", svn_url
150 150 end
151 151 with_credentials "dlopper", "wrong" do
152 152 assert_failure "ls", svn_url
153 153 end
154 154 end
155 155
156 156 def test_anonymous_read_should_fail_with_login_required
157 157 assert_success "ls", svn_url
158 158 with_settings :login_required => '1' do
159 159 assert_failure "ls", svn_url
160 160 end
161 161 end
162 162
163 163 def test_authenticated_read_should_succeed_with_login_required
164 164 with_settings :login_required => '1' do
165 165 with_credentials "miscuser8", "foo" do
166 166 assert_success "ls", svn_url
167 167 end
168 168 end
169 169 end
170 170
171 def test_read_on_archived_projects_should_fail
172 Project.find(1).update_attribute :status, Project::STATUS_ARCHIVED
173 assert_failure "ls", svn_url
174 end
175
176 def test_read_on_archived_private_projects_should_fail
177 Project.find(1).update_attribute :status, Project::STATUS_ARCHIVED
178 Project.find(1).update_attribute :is_public, false
179 with_credentials "dlopper", "foo" do
180 assert_failure "ls", svn_url
181 end
182 end
183
184 def test_read_on_closed_projects_should_succeed
185 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
186 assert_success "ls", svn_url
187 end
188
189 def test_read_on_closed_private_projects_should_succeed
190 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
191 Project.find(1).update_attribute :is_public, false
192 with_credentials "dlopper", "foo" do
193 assert_success "ls", svn_url
194 end
195 end
196
197 def test_commit_on_closed_projects_should_fail
198 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
199 Role.find(2).add_permission! :commit_access
200 with_credentials "dlopper", "foo" do
201 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
202 end
203 end
204
205 def test_commit_on_closed_private_projects_should_fail
206 Project.find(1).update_attribute :status, Project::STATUS_CLOSED
207 Project.find(1).update_attribute :is_public, false
208 Role.find(2).add_permission! :commit_access
209 with_credentials "dlopper", "foo" do
210 assert_failure "mkdir --message Creating_a_directory", svn_url(random_filename)
211 end
212 end
213
171 214 if ldap_configured?
172 215 def test_user_with_ldap_auth_source_should_authenticate_with_ldap_credentials
173 216 ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
174 217 ldap_user.login = 'example1'
175 218 ldap_user.save!
176 219
177 220 with_settings :login_required => '1' do
178 221 with_credentials "example1", "123456" do
179 222 assert_success "ls", svn_url
180 223 end
181 224 end
182 225
183 226 with_settings :login_required => '1' do
184 227 with_credentials "example1", "wrong" do
185 228 assert_failure "ls", svn_url
186 229 end
187 230 end
188 231 end
189 232 end
190 233
191 234 def test_checkout
192 235 Dir.mktmpdir do |dir|
193 236 assert_success "checkout", svn_url, dir
194 237 end
195 238 end
196 239
197 240 def test_read_commands
198 241 assert_success "info", svn_url
199 242 assert_success "ls", svn_url
200 243 assert_success "log", svn_url
201 244 end
202 245
203 246 def test_write_commands
204 247 Role.find(2).add_permission! :commit_access
205 248 filename = random_filename
206 249
207 250 Dir.mktmpdir do |dir|
208 251 assert_success "checkout", svn_url, dir
209 252 Dir.chdir(dir) do
210 253 # creates a file in the working copy
211 254 f = File.new(File.join(dir, filename), "w")
212 255 f.write "test file content"
213 256 f.close
214 257
215 258 assert_success "add", filename
216 259 with_credentials "dlopper", "foo" do
217 260 assert_success "commit --message Committing_a_file"
218 261 assert_success "copy --message Copying_a_file", svn_url(filename), svn_url("#{filename}_copy")
219 262 assert_success "delete --message Deleting_a_file", svn_url(filename)
220 263 assert_success "mkdir --message Creating_a_directory", svn_url("#{filename}_dir")
221 264 end
222 265 assert_success "update"
223 266
224 267 # checks that the working copy was updated
225 268 assert File.exists?(File.join(dir, "#{filename}_copy"))
226 269 assert File.directory?(File.join(dir, "#{filename}_dir"))
227 270 end
228 271 end
229 272 end
230 273
231 274 def test_read_invalid_repo_should_fail
232 275 assert_failure "ls", svn_url("invalid")
233 276 end
234 277
235 278 protected
236 279
237 280 def execute(*args)
238 281 a = [SVN_BIN, "--no-auth-cache --non-interactive"]
239 282 a << "--username #{username}" if username
240 283 a << "--password #{password}" if password
241 284
242 285 super a, *args
243 286 end
244 287
245 288 def svn_url(path=nil)
246 289 host = ENV['REDMINE_TEST_DAV_SERVER'] || '127.0.0.1'
247 290 url = "http://#{host}/svn/ecookbook"
248 291 url << "/#{path}" if path
249 292 url
250 293 end
251 294 end
General Comments 0
You need to be logged in to leave comments. Login now