lib/Ius/Dav/Htpasswd.pm
branchfoerste
changeset 65 85a89053a279
parent 64 a14d0bfd2e21
equal deleted inserted replaced
64:a14d0bfd2e21 65:85a89053a279
     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: