Apache-AuthenLDAP

 view release on metacpan or  search on metacpan

AuthenLDAP.pm  view on Meta::CPAN

# Contributor under this Agreement, whether expressly, by implication,
# estoppel or otherwise. All rights in the Program not expressly
# granted under this Agreement are reserved.
#
# This Agreement is governed by the laws of the State of New York and
# the intellectual property laws of the United States of America. No
# party to this Agreement will bring a legal action under this
# Agreement more than one year after the cause of action arose. Each
# party waives its rights to a jury trial in any resulting litigation.
#
###############################################################################


# Package name
package Apache::AuthenLDAP;


# Required packages
use strict;
use Apache::Constants qw(:common);
use Apache::Log       qw();
use Date::Calc        qw(Date_to_Days Today);
use Net::LDAP         qw(:all);


# Global variables
$Apache::AuthenLDAP::VERSION = '1.00';


###############################################################################
###############################################################################
# handler: hook into Apache/mod_perl API
###############################################################################
###############################################################################
sub handler {
  my $r = shift;
  return OK unless $r->is_initial_req; # only the first internal request
  my ($res, $sent_pwd) = $r->get_basic_auth_pw;
  return $res if $res;

  my $name = $r->connection->user;
  unless ($name) {
    $r->note_basic_auth_failure;
    $r->log_reason("no username supplied", $r->uri);
    return AUTH_REQUIRED;
  }

  my $cache_result = $r->notes('AuthenCache');
  if ($cache_result eq 'hit') {
    $r->log->debug("handler: upstream cache hit for username=$name");
    return OK;
  }

  my $basedn = $r->dir_config('AuthenBaseDN') || '';
  my $ldapserver = $r->dir_config('AuthenLDAPServer') || "localhost";
  my $ldapport = $r->dir_config('AuthenLDAPPort') || 389;
  my $uidattrtype = $r->dir_config('AuthenUidAttrType') || "uid";

  my $expire = lc($r->dir_config('AuthenExpire')) || 'false';
  my $exp_attrtype = $r->dir_config('AuthenExpireAttrType') ||
    'passwordIsExpired';
  my $exp_lastmodattrtype =
    $r->dir_config('AuthenExpireLastModAttrType') ||
      'passwordModifyTimestamp';
  my $exp_time = $r->dir_config('AuthenExpireTime') ||
    186;
  my $exp_redirect = $r->dir_config('AuthenExpireRedirect') || '';

  $r->log->debug("handler: ",
		 "AuthenBaseDN - $basedn; LDAPServer - $ldapserver; ",
		 "LDAPPort - $ldapport; UiaDttrType - $uidattrtype; ",
		 "Expire - $expire; ExireAttrType - $exp_attrtype; ",
		 "ExpireLastModAttrType - $exp_lastmodattrtype; ",
		 "ExpireTime - $exp_time; ExpireRedirect - $exp_redirect");

  if ($sent_pwd eq "") {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: no password supplied", $r->uri);
    return AUTH_REQUIRED;
  }

  # Connect to the server
  my $ld;
  unless ($ld = new Net::LDAP($ldapserver, port => $ldapport)) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: LDAP Connection Failed", $r->uri);
    return SERVER_ERROR;
  }

  # Bind anonymously
  my $msg = $ld->bind;
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: LDAP Initial Bind Failed: " . $msg->code .
		   " " . $msg->error, $r->uri);
    return SERVER_ERROR;
  }

  # Create the filter and search
  my $filter = "($uidattrtype=$name)";
  $r->log->debug("handler: Using filter: $filter");
  $msg = $ld->search(base => $basedn, filter => $filter);
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: ldap search operation failed: " .
		    $msg->code . " " . $msg->error, $r->uri);
    return SERVER_ERROR;
  }

  # Did we receive any entries
  unless ($msg->count) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: username not found",$r->uri);
    return AUTH_REQUIRED;
  }

  # Only want the first if we've received more than one
  my $entry = $msg->first_entry;
  my $dn = $entry->dn;

  # Bind as the user we're authenticating
  $msg = $ld->bind($dn, password => $sent_pwd);
  unless ($msg->code == LDAP_SUCCESS) {
    $r->note_basic_auth_failure;
    $r->log_reason("user $name: password mismatch", $r->uri);
    return AUTH_REQUIRED;
  }

  $ld->unbind;

  if ($expire eq 'true') {
    # Is the password set to expired in LDAP?
    if (($entry->get($exp_attrtype))[0] eq 'true') {
      $r->log->debug("handler: password flag expired");
      $r->custom_response(FORBIDDEN, "$exp_redirect");
      return FORBIDDEN;
    }

    # Has the password passed the age limit?
    my ($modyear, $modmonth, $modday) = 
      (($entry->get($exp_lastmodattrtype))[0] =~ /(\d{4})(\d{2})(\d{2})/);
    my ($year, $month, $day) = Today([time]);
    if (Date_to_Days($year, $month, $day) -
	Date_to_Days($modyear, $modmonth, $modday) > $exp_time) {
      $r->log->debug("handler: password age expired");
      $r->custom_response(FORBIDDEN, "$exp_redirect");
      return FORBIDDEN;
    }
  }

  # Everything's A-OK
  return OK;
}

1;

__END__

# Documentation - try 'pod2text AuthenLDAP'

=head1 NAME

Apache::AuthenLDAP - mod_perl LDAP Authentication Module

=head1 SYNOPSIS

 <Directory /foo/bar>
 # Authentication Realm and Type (only Basic supported)
 AuthName "Foo Bar Authentication"
 AuthType Basic

 # Any of the following variables can be set.
 # Defaults are listed to the right.
 PerlSetVar AuthenBaseDN      o=Foo,c=Bar  # Default: Empty String ("")
 PerlSetVar AuthenLDAPServer  ldap.foo.com # Default: localhost
 PerlSetVar AuthenLDAPPort    389          # Default: 389 (standard LDAP port)
 PerlSetVar AuthenUidattrType userid       # Default: uid

 PerlAuthenHandler Apache::AuthenLDAP

 require valid-user                        # Any Valid LDAP User
                                           # Matching Attribute and Value
 </Directory>

=head1 DESCRIPTION

B<Apache::AuthenLDAP> is designed to work with mod_perl and
Net::LDAP. This module authenticates a user against an LDAP
backend. It can be combined with Apache::AuthzLDAP to provide
LDAP authorization as well.

=head1 CONFIGURATION OPTIONS

The following variables can be defined within the configuration
of Directory, Location, or Files blocks or within .htaccess
files.

=over 4

=item B<AuthenBaseDN>

The base distinguished name with which to query LDAP. By default,
the AuthenBaseDN is empty.

=back

=over 4

=item B<AuthenLDAPServer>

The hostname for the LDAP server to query. By default,
AuthenLDAPServer is set to localhost.

=back

=over 4

=item B<AuthenLDAPPort>

The port on which the LDAP server is listening. By default,
AuthenLDAPPort is set to 389.

=back

=over 4

=item B<AuthenExpire>

Password expiration enablement. By default, AuthenExpire is set to
false.

=back

=over 4

=item B<AuthenExpireAttrType>

The attribute type name that contains whether or not the password is
expired. By default, AuthenExpireAttrType is passwordIsExpired.

=back

=over 4

=item B<AuthenExpireLastModAttrType>

The attribute type name that contains the password last modified
timestamp in YYYYMMDD format.  By default AuthenExpireLastModAttrType
is set to passwordModifyTimestamp.

=back

=over 4

=item B<AuthenExpireTime>

The time in days at which a password expires. By default,
AuthenExpireTime is set to 186.

=back

=over 4

=item B<AuthenExpireRedirect>

The location to which you wish to redirect users whose passwords are
expired. If this value is left blank, the server will respond with a
401 error.

=back

=head1 NOTES

This module has hooks built into it to handle Apache::AuthenCache
version 0.04 and higher passing notes to avoid bugs in the
set_handlers() method in mod_perl versions 1.2x.

=head1 AVAILABILITY

This module is available via CPAN at
http://www.cpan.org/modules/by-authors/id/C/CG/CGILMORE/.

=head1 AUTHORS

Jason Bodnar,
Christian Gilmore <cag@us.ibm.com>

=head1 SEE ALSO

httpd(8), ldap(3), mod_perl(1), slapd(8C)

=head1 COPYRIGHT

Copyright (C) 2003 International Business Machines Corporation and
others. All Rights Reserved.

This module is free software; you can redistribute it and/or
modify it under the terms of the IBM Public License.

=cut

###############################################################################
###############################################################################
# $Log: AuthenLDAP.pm,v $
# Revision 1.14  2003/06/23 18:38:59  cgilmore
# see ChangeLog
#
# Revision 1.13  2003/06/23 18:26:18  cgilmore
# see ChangeLog
#
# Revision 1.12  2002/03/07 22:03:23  cgilmore
# see ChangeLog
#
# Revision 1.11  2001/07/17 19:59:04  cgilmore
# updated documentation
#
# Revision 1.10  2001/07/12 14:14:15  cgilmore
# See ChangeLog
#
# Revision 1.9  2001/07/12 14:06:35  cgilmore
# see ChangeLog
#
# Revision 1.8  2001/05/27 19:38:24  cgilmore
# see ChangeLog
#
# Revision 1.7  2001/01/08 17:30:58  cgilmore



( run in 0.859 second using v1.01-cache-2.11-cpan-2398b32b56e )