##// END OF EJS Templates
Comment to memory leak with Postgres and ssl....
Liwiusz Ociepa -
r1402:2eba0b66d428
parent child
Show More
@@ -1,291 +1,291
1 package Apache::Authn::Redmine;
1 package Apache::Authn::Redmine;
2
2
3 =head1 Apache::Authn::Redmine
3 =head1 Apache::Authn::Redmine
4
4
5 Redmine - a mod_perl module to authenticate webdav subversion users
5 Redmine - a mod_perl module to authenticate webdav subversion users
6 against redmine database
6 against redmine database
7
7
8 =head1 SYNOPSIS
8 =head1 SYNOPSIS
9
9
10 This module allow anonymous users to browse public project and
10 This module allow anonymous users to browse public project and
11 registred users to browse and commit their project. Authentication is
11 registred users to browse and commit their project. Authentication is
12 done against the redmine database or the LDAP configured in redmine.
12 done against the redmine database or the LDAP configured in redmine.
13
13
14 This method is far simpler than the one with pam_* and works with all
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
15 database without an hassle but you need to have apache/mod_perl on the
16 svn server.
16 svn server.
17
17
18 =head1 INSTALLATION
18 =head1 INSTALLATION
19
19
20 For this to automagically work, you need to have a recent reposman.rb
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
21 (after r860) and if you already use reposman, read the last section to
22 migrate.
22 migrate.
23
23
24 Sorry ruby users but you need some perl modules, at least mod_perl2,
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
25 DBI and DBD::mysql (or the DBD driver for you database as it should
26 work on allmost all databases).
26 work on allmost all databases).
27
27
28 On debian/ubuntu you must do :
28 On debian/ubuntu you must do :
29
29
30 aptitude install libapache-dbi-perl libapache2-mod-perl2 libdbd-mysql-perl
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
32 If your Redmine users use LDAP authentication, you will also need
33 Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
33 Authen::Simple::LDAP (and IO::Socket::SSL if LDAPS is used):
34
34
35 aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
35 aptitude install libauthen-simple-ldap-perl libio-socket-ssl-perl
36
36
37 =head1 CONFIGURATION
37 =head1 CONFIGURATION
38
38
39 ## if the module isn't in your perl path
39 ## if the module isn't in your perl path
40 PerlRequire /usr/local/apache/Redmine.pm
40 PerlRequire /usr/local/apache/Redmine.pm
41 ## else
41 ## else
42 # PerlModule Apache::Authn::Redmine
42 # PerlModule Apache::Authn::Redmine
43 <Location /svn>
43 <Location /svn>
44 DAV svn
44 DAV svn
45 SVNParentPath "/var/svn"
45 SVNParentPath "/var/svn"
46
46
47 AuthType Basic
47 AuthType Basic
48 AuthName redmine
48 AuthName redmine
49 Require valid-user
49 Require valid-user
50
50
51 PerlAccessHandler Apache::Authn::Redmine::access_handler
51 PerlAccessHandler Apache::Authn::Redmine::access_handler
52 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
52 PerlAuthenHandler Apache::Authn::Redmine::authen_handler
53
53
54 ## for mysql
54 ## for mysql
55 RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
55 RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
56 ## for postgres
56 ## for postgres (there is memory leak in libpq+ssl)
57 # RedmineDSN "DBI:Pg:dbname=databasename;host=my.db.server;sslmode=disable"
57 # RedmineDSN "DBI:Pg:dbname=databasename;host=my.db.server;sslmode=disable"
58
58
59 RedmineDbUser "redmine"
59 RedmineDbUser "redmine"
60 RedmineDbPass "password"
60 RedmineDbPass "password"
61 # Optional where clause (fulltext search would be slow - and
61 ## Optional where clause (fulltext search would be slow and
62 # database dependant).
62 ## database dependant).
63 # RedmineDbWhereClause "and members.role_id IN (1,2)"
63 # RedmineDbWhereClause "and members.role_id IN (1,2)"
64 </Location>
64 </Location>
65
65
66 To be able to browse repository inside redmine, you must add something
66 To be able to browse repository inside redmine, you must add something
67 like that :
67 like that :
68
68
69 <Location /svn-private>
69 <Location /svn-private>
70 DAV svn
70 DAV svn
71 SVNParentPath "/var/svn"
71 SVNParentPath "/var/svn"
72 Order deny,allow
72 Order deny,allow
73 Deny from all
73 Deny from all
74 # only allow reading orders
74 # only allow reading orders
75 <Limit GET PROPFIND OPTIONS REPORT>
75 <Limit GET PROPFIND OPTIONS REPORT>
76 Allow from redmine.server.ip
76 Allow from redmine.server.ip
77 </Limit>
77 </Limit>
78 </Location>
78 </Location>
79
79
80 and you will have to use this reposman.rb command line to create repository :
80 and you will have to use this reposman.rb command line to create repository :
81
81
82 reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
82 reposman.rb --redmine my.redmine.server --svn-dir /var/svn --owner www-data -u http://svn.server/svn-private/
83
83
84 =head1 MIGRATION FROM OLDER RELEASES
84 =head1 MIGRATION FROM OLDER RELEASES
85
85
86 If you use an older reposman.rb (r860 or before), you need to change
86 If you use an older reposman.rb (r860 or before), you need to change
87 rights on repositories to allow the apache user to read and write
87 rights on repositories to allow the apache user to read and write
88 S<them :>
88 S<them :>
89
89
90 sudo chown -R www-data /var/svn/*
90 sudo chown -R www-data /var/svn/*
91 sudo chmod -R u+w /var/svn/*
91 sudo chmod -R u+w /var/svn/*
92
92
93 And you need to upgrade at least reposman.rb (after r860).
93 And you need to upgrade at least reposman.rb (after r860).
94
94
95 =cut
95 =cut
96
96
97 use strict;
97 use strict;
98 use warnings FATAL => 'all';
98 use warnings FATAL => 'all';
99
99
100 use DBI;
100 use DBI;
101 use Digest::SHA1;
101 use Digest::SHA1;
102 # optional module for LDAP authentication
102 # optional module for LDAP authentication
103 my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
103 my $CanUseLDAPAuth = eval("use Authen::Simple::LDAP; 1");
104
104
105 use Apache2::Module;
105 use Apache2::Module;
106 use Apache2::Access;
106 use Apache2::Access;
107 use Apache2::ServerRec qw();
107 use Apache2::ServerRec qw();
108 use Apache2::RequestRec qw();
108 use Apache2::RequestRec qw();
109 use Apache2::RequestUtil qw();
109 use Apache2::RequestUtil qw();
110 use Apache2::Const qw(:common :override :cmd_how);
110 use Apache2::Const qw(:common :override :cmd_how);
111
111
112 # use Apache2::Directive qw();
112 # use Apache2::Directive qw();
113
113
114 my @directives = (
114 my @directives = (
115 {
115 {
116 name => 'RedmineDSN',
116 name => 'RedmineDSN',
117 req_override => OR_AUTHCFG,
117 req_override => OR_AUTHCFG,
118 args_how => TAKE1,
118 args_how => TAKE1,
119 errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
119 errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
120 },
120 },
121 {
121 {
122 name => 'RedmineDbUser',
122 name => 'RedmineDbUser',
123 req_override => OR_AUTHCFG,
123 req_override => OR_AUTHCFG,
124 args_how => TAKE1,
124 args_how => TAKE1,
125 },
125 },
126 {
126 {
127 name => 'RedmineDbPass',
127 name => 'RedmineDbPass',
128 req_override => OR_AUTHCFG,
128 req_override => OR_AUTHCFG,
129 args_how => TAKE1,
129 args_how => TAKE1,
130 },
130 },
131 {
131 {
132 name => 'RedmineDbWhereClause',
132 name => 'RedmineDbWhereClause',
133 req_override => OR_AUTHCFG,
133 req_override => OR_AUTHCFG,
134 args_how => TAKE1,
134 args_how => TAKE1,
135 },
135 },
136 );
136 );
137
137
138 sub RedmineDSN { set_val('RedmineDSN', @_); }
138 sub RedmineDSN { set_val('RedmineDSN', @_); }
139 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
139 sub RedmineDbUser { set_val('RedmineDbUser', @_); }
140 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
140 sub RedmineDbPass { set_val('RedmineDbPass', @_); }
141 sub RedmineDbWhereClause {
141 sub RedmineDbWhereClause {
142 my ($self, $parms, $arg) = @_;
142 my ($self, $parms, $arg) = @_;
143 my $query = "SELECT
143 my $query = "SELECT
144 hashed_password, auth_source_id
144 hashed_password, auth_source_id
145 FROM members, projects, users
145 FROM members, projects, users
146 WHERE
146 WHERE
147 projects.id=members.project_id
147 projects.id=members.project_id
148 AND users.id=members.user_id
148 AND users.id=members.user_id
149 AND users.status=1
149 AND users.status=1
150 AND login=?
150 AND login=?
151 AND identifier=? ";
151 AND identifier=? ";
152 $self->{RedmineQuery} = $query.($arg ? $arg : "").";";
152 $self->{RedmineQuery} = $query.($arg ? $arg : "").";";
153 }
153 }
154
154
155 sub set_val {
155 sub set_val {
156 my ($key, $self, $parms, $arg) = @_;
156 my ($key, $self, $parms, $arg) = @_;
157 $self->{$key} = $arg;
157 $self->{$key} = $arg;
158 }
158 }
159
159
160 Apache2::Module::add(__PACKAGE__, \@directives);
160 Apache2::Module::add(__PACKAGE__, \@directives);
161
161
162
162
163 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
163 my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
164
164
165 sub access_handler {
165 sub access_handler {
166 my $r = shift;
166 my $r = shift;
167
167
168 unless ($r->some_auth_required) {
168 unless ($r->some_auth_required) {
169 $r->log_reason("No authentication has been configured");
169 $r->log_reason("No authentication has been configured");
170 return FORBIDDEN;
170 return FORBIDDEN;
171 }
171 }
172
172
173 my $method = $r->method;
173 my $method = $r->method;
174 return OK unless 1 == $read_only_methods{$method};
174 return OK unless 1 == $read_only_methods{$method};
175
175
176 my $project_id = get_project_identifier($r);
176 my $project_id = get_project_identifier($r);
177
177
178 $r->set_handlers(PerlAuthenHandler => [\&OK])
178 $r->set_handlers(PerlAuthenHandler => [\&OK])
179 if is_public_project($project_id, $r);
179 if is_public_project($project_id, $r);
180
180
181 return OK
181 return OK
182 }
182 }
183
183
184 sub authen_handler {
184 sub authen_handler {
185 my $r = shift;
185 my $r = shift;
186
186
187 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
187 my ($res, $redmine_pass) = $r->get_basic_auth_pw();
188 return $res unless $res == OK;
188 return $res unless $res == OK;
189
189
190 if (is_member($r->user, $redmine_pass, $r)) {
190 if (is_member($r->user, $redmine_pass, $r)) {
191 return OK;
191 return OK;
192 } else {
192 } else {
193 $r->note_auth_failure();
193 $r->note_auth_failure();
194 return AUTH_REQUIRED;
194 return AUTH_REQUIRED;
195 }
195 }
196 }
196 }
197
197
198 sub is_public_project {
198 sub is_public_project {
199 my $project_id = shift;
199 my $project_id = shift;
200 my $r = shift;
200 my $r = shift;
201
201
202 my $dbh = connect_database($r);
202 my $dbh = connect_database($r);
203 my $sth = $dbh->prepare(
203 my $sth = $dbh->prepare(
204 "SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
204 "SELECT * FROM projects WHERE projects.identifier=? and projects.is_public=true;"
205 );
205 );
206
206
207 $sth->execute($project_id);
207 $sth->execute($project_id);
208 my $ret = $sth->fetchrow_array ? 1 : 0;
208 my $ret = $sth->fetchrow_array ? 1 : 0;
209 $dbh->disconnect();
209 $dbh->disconnect();
210
210
211 $ret;
211 $ret;
212 }
212 }
213
213
214 # perhaps we should use repository right (other read right) to check public access.
214 # perhaps we should use repository right (other read right) to check public access.
215 # it could be faster BUT it doesn't work for the moment.
215 # it could be faster BUT it doesn't work for the moment.
216 # sub is_public_project_by_file {
216 # sub is_public_project_by_file {
217 # my $project_id = shift;
217 # my $project_id = shift;
218 # my $r = shift;
218 # my $r = shift;
219
219
220 # my $tree = Apache2::Directive::conftree();
220 # my $tree = Apache2::Directive::conftree();
221 # my $node = $tree->lookup('Location', $r->location);
221 # my $node = $tree->lookup('Location', $r->location);
222 # my $hash = $node->as_hash;
222 # my $hash = $node->as_hash;
223
223
224 # my $svnparentpath = $hash->{SVNParentPath};
224 # my $svnparentpath = $hash->{SVNParentPath};
225 # my $repos_path = $svnparentpath . "/" . $project_id;
225 # my $repos_path = $svnparentpath . "/" . $project_id;
226 # return 1 if (stat($repos_path))[2] & 00007;
226 # return 1 if (stat($repos_path))[2] & 00007;
227 # }
227 # }
228
228
229 sub is_member {
229 sub is_member {
230 my $redmine_user = shift;
230 my $redmine_user = shift;
231 my $redmine_pass = shift;
231 my $redmine_pass = shift;
232 my $r = shift;
232 my $r = shift;
233
233
234 my $dbh = connect_database($r);
234 my $dbh = connect_database($r);
235 my $project_id = get_project_identifier($r);
235 my $project_id = get_project_identifier($r);
236
236
237 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
237 my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
238
238
239 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
239 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
240 my $query = $cfg->{RedmineQuery};
240 my $query = $cfg->{RedmineQuery};
241 my $sth = $dbh->prepare($query);
241 my $sth = $dbh->prepare($query);
242 $sth->execute($redmine_user, $project_id);
242 $sth->execute($redmine_user, $project_id);
243
243
244 my $ret;
244 my $ret;
245 while (my @row = $sth->fetchrow_array) {
245 while (my @row = $sth->fetchrow_array) {
246 unless ($row[1]) {
246 unless ($row[1]) {
247 if ($row[0] eq $pass_digest) {
247 if ($row[0] eq $pass_digest) {
248 $ret = 1;
248 $ret = 1;
249 last;
249 last;
250 }
250 }
251 } elsif ($CanUseLDAPAuth) {
251 } elsif ($CanUseLDAPAuth) {
252 my $sthldap = $dbh->prepare(
252 my $sthldap = $dbh->prepare(
253 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
253 "SELECT host,port,tls,account,account_password,base_dn,attr_login from auth_sources WHERE id = ?;"
254 );
254 );
255 $sthldap->execute($row[1]);
255 $sthldap->execute($row[1]);
256 while (my @rowldap = $sthldap->fetchrow_array) {
256 while (my @rowldap = $sthldap->fetchrow_array) {
257 my $ldap = Authen::Simple::LDAP->new(
257 my $ldap = Authen::Simple::LDAP->new(
258 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
258 host => ($rowldap[2] == 1 || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
259 port => $rowldap[1],
259 port => $rowldap[1],
260 basedn => $rowldap[5],
260 basedn => $rowldap[5],
261 binddn => $rowldap[3] ? $rowldap[3] : "",
261 binddn => $rowldap[3] ? $rowldap[3] : "",
262 bindpw => $rowldap[4] ? $rowldap[4] : "",
262 bindpw => $rowldap[4] ? $rowldap[4] : "",
263 filter => "(".$rowldap[6]."=%s)"
263 filter => "(".$rowldap[6]."=%s)"
264 );
264 );
265 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
265 $ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass));
266 }
266 }
267 $sthldap->finish();
267 $sthldap->finish();
268 }
268 }
269 }
269 }
270 $sth->finish();
270 $sth->finish();
271 $dbh->disconnect();
271 $dbh->disconnect();
272
272
273 $ret;
273 $ret;
274 }
274 }
275
275
276 sub get_project_identifier {
276 sub get_project_identifier {
277 my $r = shift;
277 my $r = shift;
278
278
279 my $location = $r->location;
279 my $location = $r->location;
280 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
280 my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
281 $identifier;
281 $identifier;
282 }
282 }
283
283
284 sub connect_database {
284 sub connect_database {
285 my $r = shift;
285 my $r = shift;
286
286
287 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
287 my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
288 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
288 return DBI->connect($cfg->{RedmineDSN}, $cfg->{RedmineDbUser}, $cfg->{RedmineDbPass});
289 }
289 }
290
290
291 1;
291 1;
General Comments 0
You need to be logged in to leave comments. Login now