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