From fe1a91cd0621e40da8d70963f9f87ca50453f7ee Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 1 Sep 2010 16:58:14 -0500 Subject: initial (untested) implementation --- dist.ini | 4 ++ lib/Plack/Middleware/Auth/Htpasswd.pm | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/dist.ini b/dist.ini index 5e0580a..1684e85 100644 --- a/dist.ini +++ b/dist.ini @@ -7,3 +7,7 @@ copyright_holder = Jesse Luehrs dist = Plack-Middleware-Auth-Htpasswd [Prereq] +Authen::Htpasswd = 0 +MIME::Base64 = 0 +Path::Class = 0 +Plack = 0 diff --git a/lib/Plack/Middleware/Auth/Htpasswd.pm b/lib/Plack/Middleware/Auth/Htpasswd.pm index e69de29..087efa3 100644 --- a/lib/Plack/Middleware/Auth/Htpasswd.pm +++ b/lib/Plack/Middleware/Auth/Htpasswd.pm @@ -0,0 +1,81 @@ +package Plack::Middleware::Auth::Htpasswd; +use strict; +use warnings; +use base 'Plack::Middleware'; +use Plack::Util::Accessor qw(realm file file_root); +use Plack::Request; + +use Authen::Htpasswd; +use MIME::Base64; +use Path::Class; + +sub prepare_app { + my $self = shift; + die "must specify either file or file_root" + unless defined $self->file || $self->file_root; +} + +sub call { + my($self, $env) = @_; + my $auth = $env->{HTTP_AUTHORIZATION}; + return $self->unauthorized + unless $auth && $auth =~ /^Basic (.*)$/; + + my $auth_string = $1; + my ($user, $pass) = split /:/, (MIME::Base64::decode($auth_string) || ":"); + $pass = '' unless defined $pass; + + if ($self->authenticate($env, $user, $pass)) { + $env->{REMOTE_USER} = $user; + return $self->app->($env); + } + else { + return $self->unauthorized; + } +} + +sub _check_password { + my $self = shift; + my ($file, $user, $pass) = @_; + return Authen::Htpasswd->new($file)->check_user_password($user, $pass); +} + +sub authenticate { + my $self = shift; + my ($env, $user, $pass) = @_; + + return $self->_check_password($self->file, $user, $pass) + if defined $self->file; + + my $req = Plack::Request->new($env); + my $dir = dir($self->file_root); + my @htpasswd = reverse + map { $_->file('.htpasswd')->stringify } + map { $dir = $dir->subdir($_) } + split m{/}, $req->path; + + for my $htpasswd (@htpasswd) { + next unless -f $htpasswd && -r _; + return $self->_check_password($htpasswd, $user, $pass); + } + + return; +} + +sub unauthorized { + my $self = shift; + my $body = 'Authorization required'; + return [ + 401, + [ + 'Content-Type' => 'text/plain', + 'Content-Length' => length $body, + 'WWW-Authenticate' => 'Basic realm="' + . ($self->realm || "restricted area") + . '"' + ], + [ $body ], + ]; +} + +1; -- cgit v1.2.3