Revision 916
Added by Nicolas Chuche over 17 years ago
- add Redmine.pm to authenticate with mod_perl
- add a --test option in reposman.rb
- change owner right to fit with apache write access to repositories
- add a deprecated warning in reposman.pl
trunk/extra/svn/Redmine.pm | ||
---|---|---|
1 |
package Apache::Authn::Redmine; |
|
2 |
|
|
3 |
=head1 Apache::Authn::Redmine |
|
4 |
|
|
5 |
Redmine - a mod_perl module to authenticate webdav subversion users |
|
6 |
against redmine database |
|
7 |
|
|
8 |
=head1 SYNOPSIS |
|
9 |
|
|
10 |
This module allow anonymous users to browse public project and |
|
11 |
registred users to browse and commit their project. authentication is |
|
12 |
done on the redmine database. |
|
13 |
|
|
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 |
|
16 |
svn server. |
|
17 |
|
|
18 |
=head1 INSTALLATION |
|
19 |
|
|
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 |
|
22 |
migrate. |
|
23 |
|
|
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 |
|
26 |
work on allmost all databases). |
|
27 |
|
|
28 |
On debian/ubuntu you must do : |
|
29 |
|
|
30 |
aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl |
|
31 |
|
|
32 |
=head1 CONFIGURATION |
|
33 |
|
|
34 |
## if the module isn't in your perl path |
|
35 |
PerlRequire /usr/local/apache/Redmine.pm |
|
36 |
## else |
|
37 |
# PerlModule Apache::Authn::Redmine |
|
38 |
<Location /svn> |
|
39 |
DAV svn |
|
40 |
SVNParentPath "/var/svn" |
|
41 |
|
|
42 |
AuthType Basic |
|
43 |
AuthName redmine |
|
44 |
Require valid-user |
|
45 |
|
|
46 |
PerlAccessHandler Apache::Authn::Redmine::access_handler |
|
47 |
PerlAuthenHandler Apache::Authn::Redmine::authen_handler |
|
48 |
|
|
49 |
## for mysql |
|
50 |
PerlSetVar dsn DBI:mysql:database=databasename;host=my.db.server |
|
51 |
## for postgres |
|
52 |
# PerlSetVar dsn DBI:Pg:dbname=databasename;host=my.db.server |
|
53 |
|
|
54 |
PerlSetVar db_user redmine |
|
55 |
PerlSetVar db_pass password |
|
56 |
</Location> |
|
57 |
|
|
58 |
To be able to browse repository inside redmine, you must add something |
|
59 |
like that : |
|
60 |
|
|
61 |
<Location /svn-private> |
|
62 |
DAV svn |
|
63 |
SVNParentPath "/var/svn" |
|
64 |
Order deny,allow |
|
65 |
Deny from all |
|
66 |
# only allow reading orders |
|
67 |
<Limit GET PROPFIND OPTIONS REPORT> |
|
68 |
Allow from redmine.server.ip |
|
69 |
</Limit> |
|
70 |
</Location> |
|
71 |
|
|
72 |
and you will have to use this reposman.rb command line to create repository : |
|
73 |
|
|
74 |
reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/ |
|
75 |
|
|
76 |
=head1 MIGRATION FROM OLDER RELEASES |
|
77 |
|
|
78 |
If you use an older reposman.rb (r860 or before), you need to change |
|
79 |
rights on repositories to allow the apache user to read and write |
|
80 |
S<them :> |
|
81 |
|
|
82 |
sudo chown -R www-data /var/svn/* |
|
83 |
sudo chmod -R u+w /var/svn/* |
|
84 |
|
|
85 |
And you need to upgrade at least reposman.rb (after r860). |
|
86 |
|
|
87 |
=cut |
|
88 |
|
|
89 |
use strict; |
|
90 |
|
|
91 |
use DBI; |
|
92 |
use Digest::SHA1; |
|
93 |
|
|
94 |
use Apache2::Module; |
|
95 |
use Apache2::Access; |
|
96 |
use Apache2::ServerRec qw(); |
|
97 |
use Apache2::RequestRec qw(); |
|
98 |
use Apache2::RequestUtil qw(); |
|
99 |
use Apache2::Const qw(:common); |
|
100 |
# use Apache2::Directive qw(); |
|
101 |
|
|
102 |
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; |
|
103 |
|
|
104 |
sub access_handler { |
|
105 |
my $r = shift; |
|
106 |
|
|
107 |
unless ($r->some_auth_required) { |
|
108 |
$r->log_reason("No authentication has been configured"); |
|
109 |
return FORBIDDEN; |
|
110 |
} |
|
111 |
|
|
112 |
my $method = $r->method; |
|
113 |
return OK unless 1 == $read_only_methods{$method}; |
|
114 |
|
|
115 |
my $project_id = get_project_identifier($r); |
|
116 |
|
|
117 |
$r->set_handlers(PerlAuthenHandler => [\&OK]) |
|
118 |
if is_public_project($project_id, $r); |
|
119 |
|
|
120 |
return OK |
|
121 |
} |
|
122 |
|
|
123 |
sub authen_handler { |
|
124 |
my $r = shift; |
|
125 |
|
|
126 |
my ($res, $redmine_pass) = $r->get_basic_auth_pw(); |
|
127 |
return $res unless $res == OK; |
|
128 |
|
|
129 |
if (is_member($r->user, $redmine_pass, $r)) { |
|
130 |
return OK; |
|
131 |
} else { |
|
132 |
$r->note_auth_failure(); |
|
133 |
return AUTH_REQUIRED; |
|
134 |
} |
|
135 |
} |
|
136 |
|
|
137 |
sub is_public_project { |
|
138 |
my $project_id = shift; |
|
139 |
my $r = shift; |
|
140 |
|
|
141 |
my $dbh = connect_database($r); |
|
142 |
my $sth = $dbh->prepare( |
|
143 |
"SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;" |
|
144 |
); |
|
145 |
|
|
146 |
$sth->execute($project_id); |
|
147 |
my $ret = $sth->fetchrow_array ? 1 : 0; |
|
148 |
$dbh->disconnect(); |
|
149 |
|
|
150 |
$ret; |
|
151 |
} |
|
152 |
|
|
153 |
# perhaps we should use repository right (other read right) to check public access. |
|
154 |
# it could be faster BUT it doesn't work for the moment. |
|
155 |
# sub is_public_project_by_file { |
|
156 |
# my $project_id = shift; |
|
157 |
# my $r = shift; |
|
158 |
|
|
159 |
# my $tree = Apache2::Directive::conftree(); |
|
160 |
# my $node = $tree->lookup('Location', $r->location); |
|
161 |
# my $hash = $node->as_hash; |
|
162 |
|
|
163 |
# my $svnparentpath = $hash->{SVNParentPath}; |
|
164 |
# my $repos_path = $svnparentpath . "/" . $project_id; |
|
165 |
# return 1 if (stat($repos_path))[2] & 00007; |
|
166 |
# } |
|
167 |
|
|
168 |
sub is_member { |
|
169 |
my $redmine_user = shift; |
|
170 |
my $redmine_pass = shift; |
|
171 |
my $r = shift; |
|
172 |
|
|
173 |
my $dbh = connect_database($r); |
|
174 |
my $project_id = get_project_identifier($r); |
|
175 |
|
|
176 |
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); |
|
177 |
|
|
178 |
my $sth = $dbh->prepare( |
|
179 |
"SELECT hashed_password FROM members, projects, users WHERE projects.id=members.project_id AND users.id=members.user_id AND login=? AND identifier=?;" |
|
180 |
); |
|
181 |
$sth->execute($redmine_user, $project_id); |
|
182 |
|
|
183 |
my $ret; |
|
184 |
while (my @row = $sth->fetchrow_array) { |
|
185 |
if ($row[0] eq $pass_digest) { |
|
186 |
$ret = 1; |
|
187 |
last; |
|
188 |
} |
|
189 |
} |
|
190 |
$dbh->disconnect(); |
|
191 |
|
|
192 |
$ret; |
|
193 |
} |
|
194 |
|
|
195 |
sub get_project_identifier { |
|
196 |
my $r = shift; |
|
197 |
|
|
198 |
my $location = $r->location; |
|
199 |
my ($identifier) = $r->uri =~ m{$location/*([^/]+)}; |
|
200 |
$identifier; |
|
201 |
} |
|
202 |
|
|
203 |
sub connect_database { |
|
204 |
my $r = shift; |
|
205 |
|
|
206 |
my ($dsn, $db_user, $db_pass) = map { $r->dir_config($_) } qw/dsn db_user db_pass/; |
|
207 |
return DBI->connect($dsn, $db_user, $db_pass); |
|
208 |
} |
|
209 |
|
|
210 |
1; |
trunk/extra/svn/reposman.pl | ||
---|---|---|
23 | 23 |
|
24 | 24 |
$VERSION = "1.0"; |
25 | 25 |
|
26 |
my $warning = "This program is now deprecated. Use the reposman.rb for new features"; |
|
27 |
print STDERR "*" x length($warning), "\n", |
|
28 |
$warning, "\n", |
|
29 |
"*" x length($warning), "\n\n"; |
|
30 |
|
|
26 | 31 |
my %opts = (verbose => 0); |
27 | 32 |
GetOptions(\%opts, 'verbose|v+', 'version|V', 'help|h', 'man|m', 'quiet|q', 'svn-dir|s=s', 'redmine-host|r=s') or pod2usage(2); |
28 | 33 |
|
trunk/extra/svn/reposman.rb | ||
---|---|---|
37 | 37 |
# -u file:///var/svn/ # if the repository is local |
38 | 38 |
# if this option isn't set, reposman won't register the repository |
39 | 39 |
# |
40 |
# -t, --test |
|
41 |
# only show what should be done |
|
40 | 42 |
# |
41 | 43 |
# -h, --help: |
42 | 44 |
# show help and exit |
... | ... | |
64 | 66 |
['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT], |
65 | 67 |
['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT], |
66 | 68 |
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT], |
69 |
['--test', '-t', GetoptLong::NO_ARGUMENT], |
|
67 | 70 |
['--verbose', '-v', GetoptLong::NO_ARGUMENT], |
68 | 71 |
['--version', '-V', GetoptLong::NO_ARGUMENT], |
69 | 72 |
['--help' , '-h', GetoptLong::NO_ARGUMENT], |
... | ... | |
76 | 79 |
$repos_base = '' |
77 | 80 |
$svn_owner = 'root' |
78 | 81 |
$svn_url = false |
82 |
$test = false |
|
79 | 83 |
|
80 | 84 |
def log(text,level=0, exit=false) |
81 | 85 |
return if $quiet or level > $verbose |
... | ... | |
91 | 95 |
when '--owner'; $svn_owner = arg.dup |
92 | 96 |
when '--url'; $svn_url = arg.dup |
93 | 97 |
when '--verbose'; $verbose += 1 |
98 |
when '--test'; $test = true |
|
94 | 99 |
when '--version'; puts Version; exit |
95 | 100 |
when '--help'; RDoc::usage |
96 | 101 |
when '--quiet'; $quiet = true |
... | ... | |
100 | 105 |
exit 1 |
101 | 106 |
end |
102 | 107 |
|
108 |
if $test |
|
109 |
log("running in test mode") |
|
110 |
end |
|
111 |
|
|
103 | 112 |
$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/) |
104 | 113 |
|
105 | 114 |
if ($redmine_host.empty? or $repos_base.empty?) |
... | ... | |
136 | 145 |
yield if block_given? |
137 | 146 |
else |
138 | 147 |
uid, gid = Etc.getpwnam($svn_owner).uid, Etc.getgrnam(project.identifier).gid |
139 |
right = project.is_public ? 0575 : 0570
|
|
148 |
right = project.is_public ? 0775 : 0770
|
|
140 | 149 |
yield if block_given? |
141 | 150 |
Find.find(repos_path) do |f| |
142 | 151 |
File.chmod right, f |
... | ... | |
176 | 185 |
owner = owner_name(repos_path) |
177 | 186 |
next if project.is_public == other_read and owner == $svn_owner |
178 | 187 |
|
188 |
if $test |
|
189 |
log("\tchange mode on #{repos_path}") |
|
190 |
next |
|
191 |
end |
|
192 |
|
|
179 | 193 |
begin |
180 | 194 |
set_owner_and_rights(project, repos_path) |
181 | 195 |
rescue Errno::EPERM => e |
... | ... | |
186 | 200 |
log("\tmode change on #{repos_path}"); |
187 | 201 |
|
188 | 202 |
else |
189 |
project.is_public ? File.umask(0202) : File.umask(0207)
|
|
203 |
project.is_public ? File.umask(0002) : File.umask(0007)
|
|
190 | 204 |
|
205 |
if $test |
|
206 |
log("\tcreate repository #{repos_path}") |
|
207 |
log("\trepository #{repos_path} registered in Redmine with url #{$svn_url}#{project.identifier}") if $svn_url; |
|
208 |
next |
|
209 |
end |
|
210 |
|
|
191 | 211 |
begin |
192 | 212 |
set_owner_and_rights(project, repos_path) do |
193 | 213 |
raise "svnadmin create #{repos_path} failed" unless system("svnadmin", "create", repos_path) |
Also available in: Unified diff