1 |
|
2 # Copyright (C) 2011 Matthias Förste |
|
3 # |
|
4 # This program is free software: you can redistribute it and/or modify |
|
5 # it under the terms of the GNU General Public License as published by |
|
6 # the Free Software Foundation, either version 3 of the License, or |
|
7 # (at your option) any later version. |
|
8 # |
|
9 # This program is distributed in the hope that it will be useful, |
|
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 # GNU General Public License for more details. |
|
13 # |
|
14 # You should have received a copy of the GNU General Public License |
|
15 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 # |
|
17 # Matthias Förste <foerste@schlittermann.de> |
|
18 |
|
19 =encoding utf8 |
|
20 =cut |
|
21 |
|
22 package Ius::Dav::Htpasswd; |
|
23 |
|
24 use strict; |
|
25 use warnings; |
|
26 |
|
27 use Apache::Htpasswd qw(); |
|
28 use AppConfig qw(); |
|
29 use File::Path qw(rmtree); |
|
30 use POSIX qw(); |
|
31 use String::MkPasswd qw(); |
|
32 |
|
33 BEGIN { |
|
34 |
|
35 our ( $VERSION, @ISA, @EXPORT_OK ); |
|
36 use Exporter; |
|
37 |
|
38 # set the version for version checking |
|
39 $VERSION = 0.2; |
|
40 |
|
41 @ISA = qw(Exporter); |
|
42 @EXPORT_OK = qw(readconfig mkpasswd useradd userdel userexpiry usage); |
|
43 } |
|
44 |
|
45 sub usage { |
|
46 |
|
47 use Pod::Usage; |
|
48 use Pod::Find qw(pod_where); |
|
49 |
|
50 pod2usage( -input => pod_where( { -inc => 1 }, __PACKAGE__ ), @_ ); |
|
51 |
|
52 } |
|
53 |
|
54 sub readconfig { |
|
55 |
|
56 my $conf = new AppConfig( |
|
57 qw( |
|
58 expiry=i |
|
59 expiry_min=i |
|
60 expiry_max=i |
|
61 dav_base_local=s |
|
62 dav_base_remote=s |
|
63 htpasswd=s |
|
64 conf_d=s |
|
65 www_user=s |
|
66 www_group=s |
|
67 master_user=s) |
|
68 ) or die 'Failed to read config!'; |
|
69 $conf->file($_) |
|
70 for grep -e, map "$_/ius-dav-htpasswd.conf", |
|
71 qw(/etc/ius-dav-htpasswd /usr/local/etc/ius-dav-htpasswd ~/.ius-dav-htpasswd .); |
|
72 return { $conf->varlist('.') }; |
|
73 |
|
74 } |
|
75 |
|
76 sub validate { |
|
77 |
|
78 my ( $conf, $user, $expiry ) = @_; |
|
79 |
|
80 return unless $user =~ /^[[:alnum:]_]+$/; |
|
81 |
|
82 if ( defined $expiry ) { |
|
83 return unless $expiry =~ /^[0-9]+$/; |
|
84 return |
|
85 unless $expiry >= $conf->{expiry_min} |
|
86 and $expiry <= $conf->{expiry_max}; |
|
87 } |
|
88 |
|
89 return 1; |
|
90 |
|
91 } |
|
92 |
|
93 sub useradd { |
|
94 |
|
95 my ( $conf, $user, $pass, $expiry ) = @_; |
|
96 |
|
97 for ( |
|
98 qw(expiry expiry_min expiry_max dav_base_local htpasswd conf_d www_user www_group) |
|
99 ) |
|
100 { |
|
101 die "Can't determine '$_' - please check configuration" |
|
102 unless defined $conf->{$_}; |
|
103 } |
|
104 |
|
105 $expiry = $conf->{expiry} unless defined $expiry and $expiry ne ''; |
|
106 die 'Invalid input' unless validate $conf, $user, $expiry; |
|
107 |
|
108 my $user_dir = "$conf->{dav_base_local}/$user"; |
|
109 mkdir "$user_dir" or die "Can't mkdir '$user_dir': $!"; |
|
110 |
|
111 my ( $www_user, $www_group ) = @{$conf}{qw(www_user www_group)}; |
|
112 my $www_uid = getpwnam $www_user or die "Can't getpwnam '$www_user'"; |
|
113 my $www_gid = getgrnam $www_group or die "Can't getgrnam '$www_group'"; |
|
114 chown $www_uid, $www_gid, "$user_dir" |
|
115 or die "Can't chown, '$www_uid', '$www_gid', '$user_dir': $!"; |
|
116 |
|
117 my $htpasswd_file = $conf->{htpasswd}; |
|
118 unless ( -e $htpasswd_file ) { |
|
119 open H, '>>', $htpasswd_file or die "Can't create '$htpasswd_file': $!"; |
|
120 close H; |
|
121 } |
|
122 |
|
123 my $htpasswd = new Apache::Htpasswd $htpasswd_file; |
|
124 $htpasswd->htpasswd( $user, $pass ) |
|
125 or die $htpasswd->error; |
|
126 $htpasswd->writeInfo( $user, time + 24 * 60 * 60 * $expiry ) |
|
127 or die $htpasswd->error; |
|
128 |
|
129 my $master_user = $conf->{master_user}; |
|
130 my $conf_file = "$conf->{conf_d}/$user.conf"; |
|
131 (my $loc = $conf->{dav_base_remote}) =~ s|^[^:]+://[^/]+||; |
|
132 $loc .= "/$user"; |
|
133 open C, '>', $conf_file or die "Can't open '$conf_file': $!"; |
|
134 print C <<EOC; |
|
135 <Directory "$user_dir"> |
|
136 Dav On |
|
137 Order Allow,Deny |
|
138 Allow From All |
|
139 AuthType Basic |
|
140 AuthName "$user" |
|
141 AuthUserFile "$htpasswd_file" |
|
142 Require user $master_user $user |
|
143 # don't allow script execution |
|
144 Options Indexes |
|
145 AllowOverride None |
|
146 </Directory> |
|
147 <Location "$loc"> |
|
148 Order Allow,Deny |
|
149 Allow From All |
|
150 </Location> |
|
151 # vi:ft=apache |
|
152 EOC |
|
153 close C; |
|
154 |
|
155 0 == system qw(apache2ctl graceful) |
|
156 or die "Can't 'apache2ctl graceful'!"; |
|
157 |
|
158 return $pass; |
|
159 |
|
160 } |
|
161 |
|
162 sub mkpasswd { return String::MkPasswd::mkpasswd -minspecial => 0; } |
|
163 |
|
164 sub userdel { |
|
165 |
|
166 my ( $conf, $user ) = @_; |
|
167 |
|
168 my $rc = 0; |
|
169 |
|
170 for (qw(dav_base_local htpasswd conf_d)) { |
|
171 die "Can't determine '$_' - please check configuration" |
|
172 unless defined $conf->{$_}; |
|
173 } |
|
174 |
|
175 # avoid 'Found = in conditional, should be ==' warnings |
|
176 no warnings qw(syntax); |
|
177 my $user_dir = "$conf->{dav_base_local}/$user"; |
|
178 my $err; |
|
179 rmtree( $user_dir, error => $err ) |
|
180 or $rc = -1 |
|
181 and warn "Error(s) occurred during rmtree '$user_dir': ", |
|
182 defined $err ? @{$err} : ''; |
|
183 |
|
184 my $htpasswd_file = $conf->{htpasswd}; |
|
185 my $htpasswd = new Apache::Htpasswd $htpasswd_file; |
|
186 $htpasswd->htDelete($user) |
|
187 or $rc = -1 and warn "Can't htdelete '$user': ", $htpasswd->error; |
|
188 |
|
189 my $conf_file = "$conf->{conf_d}/$user.conf"; |
|
190 unlink $conf_file |
|
191 or $rc = -1 and warn "Can't unlink '$conf_file': $!"; |
|
192 |
|
193 0 == system qw(apache2ctl graceful) |
|
194 or $rc = -1 and warn "Can't 'apache2ctl graceful'!"; |
|
195 |
|
196 return $rc; |
|
197 |
|
198 } |
|
199 |
|
200 sub userexpiry { |
|
201 |
|
202 my ($conf) = @_; |
|
203 |
|
204 my $rc = 0; |
|
205 |
|
206 for (qw(htpasswd)) { |
|
207 die "Can't determine '$_' - please check configuration" |
|
208 unless defined $conf->{$_}; |
|
209 } |
|
210 |
|
211 my $htpasswd_file = $conf->{htpasswd}; |
|
212 my $htpasswd = new Apache::Htpasswd $htpasswd_file; |
|
213 # empty @users does not indicate failure |
|
214 my @users = $htpasswd->fetchUsers; |
|
215 my $now = time; |
|
216 |
|
217 for my $u (@users) { |
|
218 if ( my $e = $htpasswd->fetchInfo($u) ) { |
|
219 userdel( $conf, $u ) |
|
220 and warn "Error(s) occured during 'userdel $conf, $u'\n" |
|
221 if $now >= $e; |
|
222 } |
|
223 else { |
|
224 warn "Can't get expiry for '$u': ", $htpasswd->error, "\n"; |
|
225 } |
|
226 } |
|
227 |
|
228 return $rc; |
|
229 |
|
230 } |
|
231 |
|
232 1; |
|
233 |
|
234 __END__ |
|
235 |
|
236 =pod |
|
237 |
|
238 =head1 NAME |
|
239 |
|
240 ius-dav-useradd |
|
241 |
|
242 ius-dav-useradd.cgi |
|
243 |
|
244 ius-dav-userdel |
|
245 |
|
246 ius-dav-userexpiry |
|
247 |
|
248 Ius::Dav::Htpasswd - Add dav users to htpasswd and remove them after |
|
249 expiration. |
|
250 |
|
251 =head1 SYNOPSIS |
|
252 |
|
253 ius-dav-useradd |
|
254 -u|--user user |
|
255 [-e|--expiry expiry] |
|
256 |
|
257 ius-dav-userdel |
|
258 -u|--user user |
|
259 |
|
260 ius-dav-userexpiry |
|
261 |
|
262 common options |
|
263 [-m|--man] |
|
264 [-h|--help] |
|
265 |
|
266 =head1 DESCRIPTION |
|
267 |
|
268 =head2 ius-dav-useradd |
|
269 |
|
270 Make a directory for the user. Chown that directory to the webserver user and |
|
271 group. Add the user to an htpasswd file. Add expiry information to that |
|
272 htpasswd file. Place a config snippet for the users directory inside a |
|
273 directory (which is included from the apache config). Reload apache. |
|
274 |
|
275 =head2 ius-dav-useradd.cgi |
|
276 |
|
277 This is a CGI Wrapper around ius-dav-useradd. |
|
278 |
|
279 =head2 ius-dav-userdel |
|
280 |
|
281 Removes the directory of the user. Removes the user from the htpasswd file. |
|
282 Removes the config snippet for the users directory. Reload apache. |
|
283 |
|
284 =head2 ius-dav-userexpiry |
|
285 |
|
286 Check the htpasswd file and run deletion for any expired users found. |
|
287 |
|
288 =head1 OPTIONS |
|
289 |
|
290 =over |
|
291 |
|
292 =item B<-u|--user> I<user> |
|
293 |
|
294 The name of the user to add or remove. |
|
295 |
|
296 =item B<-e|--expiry> I<expiry> |
|
297 |
|
298 The time in days after which an added user will expire. Defaults to 1. |
|
299 |
|
300 =back |
|
301 |
|
302 =head1 FILES |
|
303 |
|
304 F</etc/ius-dav-htpasswd/ius-dav-htpasswd.conf> |
|
305 |
|
306 F</usr/local/etc/ius-dav-htpasswd/ius-dav-htpasswd.conf> |
|
307 |
|
308 F<~/.ius-dav-htpasswd/ius-dav-htpasswd.conf> |
|
309 |
|
310 F<./ius-dav-htpasswd.conf> |
|
311 |
|
312 F</srv/dav> |
|
313 |
|
314 F</etc/apache2/htpasswd> |
|
315 |
|
316 F</etc/apache2/dav.d> |
|
317 |
|
318 =head1 REQUIRES |
|
319 |
|
320 Several perl modules (should be installed automatically). Some kind of cron |
|
321 daemon to run the user expiry is recommended. |
|
322 |
|
323 =head1 AUTHOR |
|
324 |
|
325 Matthias Förste <foerste@schlittermann.de> |
|
326 |
|
327 =cut |
|
328 |
|
329 # vim:sts=4 sw=4 aw ai sm: |
|