--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/Ius/Dav/Htpasswd.pm Wed Jul 06 07:23:34 2011 +0200
@@ -0,0 +1,267 @@
+
+# Copyright (C) 2011 Matthias Förste
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Matthias Förste <foerste@schlittermann.de>
+
+=encoding utf8
+=cut
+
+package Ius::Dav::Htpasswd;
+
+use strict;
+use warnings;
+
+use Apache::Htpasswd;
+use AppConfig;
+use File::Path qw(rmtree);
+use POSIX;
+use String::MkPasswd qw(mkpasswd);
+
+BEGIN {
+
+ our ($VERSION, @ISA, @EXPORT_OK);
+ use Exporter;
+
+ # set the version for version checking
+ $VERSION = 0.1;
+
+ @ISA = qw(Exporter);
+ @EXPORT_OK = qw(readconfig useradd userdel usage);
+}
+
+sub usage {
+
+ use Pod::Usage;
+ use Pod::Find qw(pod_where);
+
+ pod2usage( -input => pod_where({-inc => 1}, __PACKAGE__), @_ );
+
+}
+
+sub readconfig {
+
+ my $conf = new AppConfig(qw(
+ expiry=i
+ expiry_min=i
+ expiry_max=i
+ dav_base=s
+ htpasswd=s
+ conf_d=s
+ www_user=s
+ www_group=s
+ master_user=s)
+ ) or die 'Failed to read config!';
+ $conf->file($_) for grep -e, map "$_/dav-htpasswd.conf", qw(/etc /usr/local/etc ~ .);
+ return { $conf->varlist('.') };
+
+}
+
+sub validate {
+
+ my ($conf, $user, $expiry) = @_;
+
+ return unless $user =~ /^[[:alpha:]_]+$/;
+
+ if (defined $expiry) {
+ return unless $expiry =~ /^[0-9]+$/;
+ return unless $expiry >= $conf->{expiry_min} and $expiry <= $conf->{expiry_max};
+ }
+
+ return 1;
+
+}
+
+sub useradd {
+
+ my ($conf, $user, $expiry) = @_;
+
+ for (qw(expiry expiry_min expiry_max dav_base htpasswd conf_d www_user www_group)) {
+ die "Can't determine '$_' - please check configuration"
+ unless defined $conf->{$_};
+ }
+
+ $expiry = $conf->{expiry} unless defined $expiry;
+ die 'Invalid input' unless validate $conf, $user, $expiry;
+
+ my $at_cmd = "at now + " . 24 * 60 * $expiry . " minutes";
+ open AT, "|$at_cmd"
+ or die "Can't open AT, '|$at_cmd': $!";
+ print AT "dav-htuserdel";
+ close AT;
+
+ my $user_dir = "$conf->{dav_base}/$user";
+ mkdir "$user_dir" or die "Can't mkdir '$user_dir': $!";
+
+ my ($www_user, $www_group) = @{$conf}{qw(www_user www_group)};
+ my $www_uid = getpwnam $www_user or die "Can't getpwnam '$www_user'";
+ my $www_gid = getgrnam $www_group or die "Can't getgrnam '$www_group'";
+ chown $www_uid, $www_gid, "$user_dir" or die "Can't chown, '$www_uid', '$www_gid', '$user_dir': $!";
+
+ my $pass = mkpasswd;
+ my $htpasswd_file = $conf->{htpasswd};
+ unless (-e $htpasswd_file ) {
+ open H, '>>', $htpasswd_file or die "Can't create '$htpasswd_file': $!";
+ close H;
+ }
+ my $htpasswd = new Apache::Htpasswd $htpasswd_file;
+ $htpasswd->htpasswd($user, $pass)
+ or die $htpasswd->error;
+
+ my $master_user = $conf->{master_user};
+ my $conf_file = "$conf->{conf_d}/$user.conf";
+ open C, '>', $conf_file or die "Can't open '$conf_file': $!";
+ print C <<EOC;
+<Directory "$user_dir">
+ Dav On
+ Order Allow,Deny
+ Allow From All
+ Deny From None
+ AuthType Basic
+ AuthName "$user"
+ AuthUserFile "$htpasswd_file"
+ Require user $master_user $user
+</Directory>
+EOC
+ close C;
+
+ 0 == system qw(apache2ctl graceful)
+ or die "Can't 'apache2ctl graceful'!";
+
+ return $pass;
+
+}
+
+sub userdel {
+
+ my ($conf, $user) = @_;
+
+ my $rc;
+
+ for (qw(dav_base htpasswd conf_d)) {
+ die "Can't determine '$_' - please check configuration"
+ unless defined $conf->{$_};
+ }
+
+ # avoid 'Found = in conditional, should be ==' warnings
+ no warnings qw(syntax);
+ my $user_dir = "$conf->{dav_base}/$user";
+ my $err;
+ rmtree($user_dir, error => $err)
+ or $rc = -1 and warn "Errors occurred during rmtree '$user_dir': ", @{$err};
+
+ my $htpasswd_file = $conf->{htpasswd};
+ my $htpasswd = new Apache::Htpasswd $htpasswd_file;
+ $htpasswd->htDelete($user)
+ or $rc = -1 and warn "Can't htdelete '$user': ", $htpasswd->error;
+
+ my $conf_file = "$conf->{conf_d}/$user.conf";
+ unlink $conf_file
+ or $rc = -1 and warn "Can't unlink '$conf_file': $!";
+
+ # maybe TODO: remove at job if it still exists (record job# during #
+ # 'useradd'?)
+
+ 0 == system qw(apache2ctl graceful)
+ or $rc =-1 and warn "Can't 'apache2ctl graceful'!";
+
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+ dav-useradd
+ dav-useradd.cgi
+ dav-userdel
+
+=head1 SYNOPSIS
+
+ dav-useradd -u|--user user
+ [-e|--expiry expiry]
+
+ dav-userdel -u|--user user
+
+ common options
+
+ -m|--man
+ -h|--help
+
+=head1 DESCRIPTION
+
+=head2 dav-useradd
+
+Add an at job to remove the user later. Make a directory for the user. Chown
+that directory to the webserver user and group. Add the user to an htpasswd
+file. Place a config snippet for the users directory inside a directory (which
+is included from the apache config, other webservers may also work). Reload
+apache (or maybe restart is required).
+
+=head2 dav-useradd.cgi
+
+Is supposed to do the same as dav-useradd.
+
+=head2 dav-userdel
+
+Removes the directory of the user. Removes the user from the htpasswd file.
+Removes the config snippet for the users directory. Removes the at job that is
+supposed to remove the user if it still exists. Reload apache (or maybe restart
+is required).
+
+=head1 OPTIONS
+
+=over
+
+=item B<-u|--user> I<user>
+
+The name of the user to add or remove.
+
+=item B<-e|--expiry> I<expiry>
+
+The time in days after which an added user will expire. Defaults to 1.
+
+=back
+
+=head1 FILES
+
+F</etc/dav-htpasswd.conf>
+
+F</usr/local/etc/dav-htpasswd.conf>
+
+F<~/dav-htpasswd.conf>
+
+F<./dav-htpasswd.conf>
+
+F</srv/dav>
+
+F</etc/apache2/htpasswd>
+
+F</etc/apache2/dav.d>
+
+=head1 REQUIRES
+
+at from the 'at' job scheduler package.
+
+=head1 AUTHOR
+
+Matthias Förste <foerste@schlittermann.de>
+
+=cut
+
+# vim:sts=4 sw=4 aw ai sm: