The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package HTTP::AppServer::Plugin::HTTPAuth;
# Plugin for HTTP::AppServer uses HTTP authentication to
# authenticate a client. The authentication works based
# on a certain handler regex.
# 2010 by Tom Kirchner

use 5.010000;
use strict;
use warnings;
use MIME::Base64;
use HTTP::AppServer::Plugin;
use base qw(HTTP::AppServer::Plugin);

our $VERSION = '0.01';

my $Logins = {};
my $URLs = [];

# called by the server when the plugin is installed
# to determine which routes are handled by the plugin
sub init
{
	my ($class, $server, %options) = @_;

	$Logins = $options{'Logins'} if exists $options{'Logins'};
	$URLs   = $options{'URLs'}   if exists $options{'URLs'};
	
	if (exists $options{'LoginsFile'}) {
		my $filename = $options{'LoginsFile'};
		
		open(FH, '<'.$filename) 
			or print STDERR "HTTPAuth: Failed to open loginsfile '$filename': $! $@\n";
		while (my $line = <FH>) {
			chomp $line;
			my ($username, $password) = split /\:/, $line;
			$Logins->{$username} = $password
				if defined $username && length $username && defined $password;
		}
		close FH;
	}

	# hash that contains all active http login accounts
	$server->set('httpauth_logins', $Logins);

	# hash that contains all restricted area URLs
	$server->set('httpauth_urls', $URLs);

	# the plugin installs a match-all URL handler
	return (
		'^(.*)$' => \&_auth,
	);
}

sub _auth
{
	my ($server, $cgi, $url) = @_;
	
	if (scalar grep { ($url =~ /$_/) == 1 } @{$server->httpauth_urls()}) {
		my $auth = $cgi->http('Authorization');
	
		my $authorized = 0;
		if (defined $auth) {
			# try to authenticate user
			my ($prefix, $encoded) = split /\s/, $auth;
			my ($username, $password) = split /\:/, decode_base64($encoded);
			if (exists $server->httpauth_logins()->{$username} && 
				  $server->httpauth_logins()->{$username} eq $password) {		
				  
				my $session_id = time().sprintf('%.0f', rand(100000000));
				$authorized = 1;
			}
		}
		
		unless ($authorized) {
			# tell client to authorizate itself
			print
				"HTTP/1.0 401 Unauthorized\r\n".
				$cgi->header(
					-WWW_Authenticate => 'Basic realm="MySite"',
				);
			return 1;
		}
	}
	return 0;
}

1;
__END__
=head1 NAME

HTTP::AppServer::Plugin::HTTPAuth - Plugin for HTTP::AppServer uses HTTP authentication to authenticate a client. The authentication works based on a certain handler regex.

=head1 SYNOPSIS

  use HTTP::AppServer;
  my $server = HTTP::AppServer->new();
  $server->plugin('HTTPAuth', Logins => {guest => '', mrx => 'pass'}, URLs => ['^\/admin']);

=head1 DESCRIPTION

Plugin for HTTP::AppServer uses HTTP authentication to authenticate a client. 
The authentication works based on a certain handler regex.

=head2 Plugin configuration

=head3 Logins => I<hash>

A hash containing the available accounts that are allowed to
access the restricted URLs, e.g.:

  ..., Logins => {guest => '', mrx => 'pass'}, ...

=head3 URLs => I<array>

This is a list of restricted URLs. When an URL is accessed that matches
any regular expression in this list, a HTTP authorization is preformed.
If the authorization fails an error page is returned. In all other
cases (URL not restricted or authorization was successful) other
handlers are allowed to process the URL.

=head3 LoginsFile => I<filename>

This can be supplied additionally to the Logins option.
The account information is then read from a file that has the
format of normal .htpasswd files, e.g.

  username1:password
  username2:password
  ...

while I<password> is a Base64 encoded password.

=head2 Installed URL handlers

HTTPAuth installs a binding to the URL '^(.*)$', which means
it matches everything. It allows for further processing after
that if the URL is not restricted (is not contained in the URLs
option when loading the plugin).

=head2 Installed server properties

=head3 httpauth_logins

This is a reference to the Logins that are configured when loading the plugin.

=head3 httpauth_urls

This is a reference to the URLs that are configured when loading the plugin.

=head2 Installed server methods

None.

=head1 SEE ALSO

HTTP::AppServer, HTTP::AppServer::Plugin

=head1 AUTHOR

Tom Kirchner, E<lt>tom@tkirchner.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2010 by Tom Kirchner

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.


=cut