Apache::AuthPOP3 - Authentication and Authorization via POP3


Apache-AuthPOP3 documentation Contained in the Apache-AuthPOP3 distribution.

Index


Code Index:

NAME

Top

Apache::AuthPOP3 - Authentication and Authorization via POP3

SYNOPSIS

Top

  # In httpd.conf or startup.pl:

  PerlModule Apache::AuthDBI

  # In httpd.conf or .htaccess:

  <Location /protected>
    AuthName POP3
    AuthType Basic
    PerlAuthenHandler Apache::AuthPOP3
    PerlSetVar        MailHost pop.example.com

    PerlSetVar        UserMap pop3user1=>realname1,pop3user2=>realname2
    Require user      pop3user1 pop3user2 pop3user3 pop3user4

    # Require valid-user
  </Location>

DESCRIPTION

Top

This module allows authentication and authorization against a POP3 server.

Received username and password are looked up in the cache. If nothing was stored in the cache with that particular username and password within the past two minutes, they are passed to the POP3 server and cached once authenticated; SHA1 checksum of password is used in caching.

After being authorized, the username or the name that maps to it based on the UserMap configuration is used to set the remote user.

CONFIGURATION

Top

MailHost (Required)

Defines the POP3 server to authenticate against.

UserMap (Optional)

If defined, the remote user is set based on this.

AUTHOR

Top

Sherwin Daganato, <sherwin at cpan.org>

SEE ALSO

Top

Apache, mod_perl, Net::POP3

COPYRIGHT & LICENSE

Top


Apache-AuthPOP3 documentation Contained in the Apache-AuthPOP3 distribution.

package Apache::AuthPOP3;

use warnings;
use strict;

our $VERSION = '0.02';

use constant MP2 => (exists $ENV{MOD_PERL_API_VERSION} and 
                     $ENV{MOD_PERL_API_VERSION} >= 2);

BEGIN {
  if ($ENV{MOD_PERL}) {
    my @constants = qw(OK DECLINED HTTP_UNAUTHORIZED);
    if (MP2) {
      require Apache2::Access;      # for note_basic_auth_failure, get_basic_auth_pw, and requires
      require Apache2::RequestUtil; # for push_handlers, and dir_config
      require Apache2::RequestRec;  # for user, and filename
      require Apache2::Log;         # for log_error
      require Apache2::Const;
      Apache2::Const->import(-compile => @constants);
    } else {
      require Apache;
      require Apache::Constants;
      Apache::Constants->import(@constants);
    }
  }
}

use Net::POP3;
use Cache::FileCache;
use Digest::SHA1 qw(sha1_hex);

sub handler {
  my $r = shift;

  $r->push_handlers(PerlAuthzHandler => \&authorize);

  # check if MailHost config variable is present
  return MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED() unless (my $mailhost = $r->dir_config('MailHost'));

  # get user's authentication credentials
  my ($res, $passwd_sent) = $r->get_basic_auth_pw;
  return $res if (MP2 and $res != Apache2::Const::OK() or !MP2 and $res != Apache::Constants::OK());
  my $user_sent = $r->user;

  my $reason = authenticate($mailhost, $user_sent, $passwd_sent);
  if ($reason) {
    $r->note_basic_auth_failure;
    $r->log_reason($reason, $r->filename);
    return MP2 ? Apache2::Const::HTTP_UNAUTHORIZED() : Apache::Constants::HTTP_UNAUTHORIZED();
  }

  return MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
}

sub authenticate {
  my ($mailhost, $user_sent, $passwd_sent) = @_;

  $user_sent and $passwd_sent or return 'either username or password is empty';

  # cache sha1-ed password
  my $cache = new Cache::FileCache({ 'namespace' => __PACKAGE__, 'default_expires_in' => 120 });
  my $passwd_cached_sha1 = $cache->get($user_sent);
  my $passwd_sent_sha1 = sha1_hex($passwd_sent);
  if (defined $passwd_cached_sha1) {
    return "user $user_sent: POP3 login failed" if $passwd_cached_sha1 ne $passwd_sent_sha1;
  } else {
    return "user $user_sent: POP3 login failed" unless Net::POP3->new($mailhost)->login($user_sent, $passwd_sent);
    $cache->set($user_sent, $passwd_sent_sha1);
  }

  return '';
}

sub authorize {
  my $r = shift;

  return MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED() unless (my $requires = $r->requires);
  my $user_sent = $r->user;

  for my $entry (@$requires) {
    my ($requirement, @rest) = split /\s+/, $entry->{requirement};
    return MP2 ? Apache2::Const::OK() : Apache::Constants::OK() if (lc $requirement eq 'valid-user');

    if (lc $requirement eq 'user') {
      foreach (@rest) { 
        if ($user_sent eq $_) {

          # change the username seen by apache to the one defined in UserMap
          if (my $usermap = $r->dir_config('UserMap')) {
            my %usermap = split /\s*(?:=>|,)\s*/, $usermap;
            $r->user($usermap{$user_sent}) if defined $usermap{$user_sent};
          }
          return MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
        }
      }
      $r->log_error("user $user_sent: invalid user");
    }

    $r->log_error("user $user_sent: failed requirement");
  }

  $r->note_basic_auth_failure;
  $r->log_reason("user $user_sent: not authorized", $r->filename);
  return MP2 ? Apache2::Const::HTTP_UNAUTHORIZED() : Apache::Constants::HTTP_UNAUTHORIZED();
}

1;

__END__