##// END OF EJS Templates
Redmine.pm for webdav authentication:...
Jean-Philippe Lang -
r1321:2bcb7820875b
parent child
Show More
@@ -1,231 +1,235
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 registred users to browse and commit their project. authentication is
12 done on the redmine database.
11 registred users to browse and commit their project. Authentication is
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 If your Redmine users use LDAP authentication, you will also need
33 Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used).
34
32 35 =head1 CONFIGURATION
33 36
34 37 ## if the module isn't in your perl path
35 38 PerlRequire /usr/local/apache/Redmine.pm
36 39 ## else
37 40 # PerlModule Apache::Authn::Redmine
38 41 <Location /svn>
39 42 DAV svn
40 43 SVNParentPath "/var/svn"
41 44
42 45 AuthType Basic
43 46 AuthName redmine
44 47 Require valid-user
45 48
46 49 PerlAccessHandler Apache::Authn::Redmine::access_handler
47 50 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
48 51
49 52 ## for mysql
50 53 PerlSetVar dsn DBI:mysql:database=databasename;host=my.db.server
51 54 ## for postgres
52 55 # PerlSetVar dsn DBI:Pg:dbname=databasename;host=my.db.server
53 56
54 57 PerlSetVar db_user redmine
55 58 PerlSetVar db_pass password
56 59 </Location>
57 60
58 61 To be able to browse repository inside redmine, you must add something
59 62 like that :
60 63
61 64 <Location /svn-private>
62 65 DAV svn
63 66 SVNParentPath "/var/svn"
64 67 Order deny,allow
65 68 Deny from all
66 69 # only allow reading orders
67 70 <Limit GET PROPFIND OPTIONS REPORT>
68 71 Allow from redmine.server.ip
69 72 </Limit>
70 73 </Location>
71 74
72 75 and you will have to use this reposman.rb command line to create repository :
73 76
74 77 reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
75 78
76 79 =head1 MIGRATION FROM OLDER RELEASES
77 80
78 81 If you use an older reposman.rb (r860 or before), you need to change
79 82 rights on repositories to allow the apache user to read and write
80 83 S<them :>
81 84
82 85 sudo chown -R www-data /var/svn/*
83 86 sudo chmod -R u+w /var/svn/*
84 87
85 88 And you need to upgrade at least reposman.rb (after r860).
86 89
87 90 =cut
88 91
89 92 use strict;
90 93
91 94 use DBI;
92 95 use Digest::SHA1;
93 use Authen::Simple::LDAP;
96 # optional module for LDAP authentication
97 my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
94 98
95 99 use Apache2::Module;
96 100 use Apache2::Access;
97 101 use Apache2::ServerRec qw();
98 102 use Apache2::RequestRec qw();
99 103 use Apache2::RequestUtil qw();
100 104 use Apache2::Const qw(:common);
101 105 # use Apache2::Directive qw();
102 106
103 107 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
104 108
105 109 sub access_handler {
106 110 my $r = shift;
107 111
108 112 unless ($r->some_auth_required) {
109 113 $r->log_reason("No authentication has been configured");
110 114 return FORBIDDEN;
111 115 }
112 116
113 117 my $method = $r->method;
114 118 return OK unless 1 == $read_only_methods{$method};
115 119
116 120 my $project_id = get_project_identifier($r);
117 121
118 122 $r->set_handlers(PerlAuthenHandler => [\&OK])
119 123 if is_public_project($project_id, $r);
120 124
121 125 return OK
122 126 }
123 127
124 128 sub authen_handler {
125 129 my $r = shift;
126 130
127 131 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
128 132 return $res unless $res == OK;
129 133
130 134 if (is_member($r->user, $redmine_pass, $r)) {
131 135 return OK;
132 136 } else {
133 137 $r->note_auth_failure();
134 138 return AUTH_REQUIRED;
135 139 }
136 140 }
137 141
138 142 sub is_public_project {
139 143 my $project_id = shift;
140 144 my $r = shift;
141 145
142 146 my $dbh = connect_database($r);
143 147 my $sth = $dbh->prepare(
144 148 "SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
145 149 );
146 150
147 151 $sth->execute($project_id);
148 152 my $ret = $sth->fetchrow_array ? 1 : 0;
149 153 $dbh->disconnect();
150 154
151 155 $ret;
152 156 }
153 157
154 158 # perhaps we should use repository right (other read right) to check public access.
155 159 # it could be faster BUT it doesn't work for the moment.
156 160 # sub is_public_project_by_file {
157 161 # my $project_id = shift;
158 162 # my $r = shift;
159 163
160 164 # my $tree = Apache2::Directive::conftree();
161 165 # my $node = $tree->lookup('Location', $r->location);
162 166 # my $hash = $node->as_hash;
163 167
164 168 # my $svnparentpath = $hash->{SVNParentPath};
165 169 # my $repos_path = $svnparentpath . "/" . $project_id;
166 170 # return 1 if (stat($repos_path))[2] & 00007;
167 171 # }
168 172
169 173 sub is_member {
170 174 my $redmine_user = shift;
171 175 my $redmine_pass = shift;
172 176 my $r = shift;
173 177
174 178 my $dbh = connect_database($r);
175 179 my $project_id = get_project_identifier($r);
176 180
177 181 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
178 182
179 183 my $sth = $dbh->prepare(
180 184 "SELECT hashed_password, auth_source_id FROM members, projects, users WHERE projects.id=members.project_id AND users.id=members.user_id AND users.status=1 AND login=? AND identifier=?;"
181 185 );
182 186 $sth->execute($redmine_user, $project_id);
183 187
184 188 my $ret;
185 189 while (my @row = $sth->fetchrow_array) {
186 190 unless ($row[1]) {
187 191 if ($row[0] eq $pass_digest) {
188 192 $ret = 1;
189 193 last;
190 194 }
191 } else {
195 } elsif ($CanUseLDAPAuth) {
192 196 my $sthldap = $dbh->prepare(
193 "SELECT host,port,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
197 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
194 198 );
195 199 $sthldap->execute($row[1]);
196 200 while (my @rowldap = $sthldap->fetchrow_array) {
197 201 my $ldap = Authen::Simple::LDAP->new(
198 host => $rowldap[0],
199 port => $rowldap[1],
200 basedn => $rowldap[4],
201 binddn => $rowldap[2] ? $rowldap[2] : "",
202 bindpw => $rowldap[3] ? $rowldap[3] : "",
203 filter => "(".$rowldap[5]."=%s)"
204 );
205 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
202 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
203 port => $rowldap[1],
204 basedn => $rowldap[5],
205 binddn => $rowldap[3] ? $rowldap[3] : "",
206 bindpw => $rowldap[4] ? $rowldap[4] : "",
207 filter => "(".$rowldap[6]."=%s)"
208 );
209 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
206 210 }
207 211 $sthldap->finish();
208 212 }
209 213 }
210 214 $sth->finish();
211 215 $dbh->disconnect();
212 216
213 217 $ret;
214 218 }
215 219
216 220 sub get_project_identifier {
217 221 my $r = shift;
218 222
219 223 my $location = $r->location;
220 224 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
221 225 $identifier;
222 226 }
223 227
224 228 sub connect_database {
225 229 my $r = shift;
226 230
227 231 my ($dsn, $db_user, $db_pass) = map { $r->dir_config($_) } qw/dsn db_user db_pass/;
228 232 return DBI->connect($dsn, $db_user, $db_pass);
229 233 }
230 234
231 235 1;
General Comments 0
You need to be logged in to leave comments. Login now