Apache-AuthzLDAP

 view release on metacpan or  search on metacpan

AuthzLDAP.pm  view on Meta::CPAN

    # security implications in a general environment where you
    # might be using someone else's handlers upstream or
    # downstream...
  my $group_sent = $r->subprocess_env("REMOTE_GROUP") ||
    $r->headers_in->{'REMOTE_GROUP'};
  my $cache_result = $r->notes('AuthzCache');
  if ($group_sent && $cache_result eq 'hit') {
    $r->log->debug("handler: upstream cache hit for ",
		   "user=$username, group=$group_sent");
    return OK;
  # }
  }

  # Clear for paranoid security precautions
  $r->subprocess_env(REMOTE_GROUP => undef);
  undef($r->headers_in->{'REMOTE_GROUP'});

  my $basedn = $r->dir_config('AuthzBaseDN');
  my $groupattrtype = $r->dir_config('AuthzGroupAttrType') || 'cn';
  my $authzldapserver = $r->dir_config('AuthzLDAPServer') || "localhost";
  my $authzldapport = $r->dir_config('AuthzLDAPPort') || 389;
  my $authenldapserver = $r->dir_config('AuthenLDAPServer') ||
    $r->dir_config('AuthzLDAPServer') || "localhost";
  my $authenldapport = $r->dir_config('AuthenLDAPPort') ||
    $r->dir_config('AuthzLDAPPort') || 389;
  my $memberattrtype = $r->dir_config('AuthzMemberAttrType') || 'member';
  my $memberattrvalue = $r->dir_config('AuthzMemberAttrValue') || 'cn';
  my $nestedattrtype = $r->dir_config('AuthzNestedAttrType') || 'member';
  my $nested_groups = $r->dir_config('AuthzNestedGroups');
  my $requirement = $r->dir_config('AuthzRequire') || 'inAGroup';
  my $uidattrtype = $r->dir_config('AuthzUidAttrType') || 'uid';
  my $userbasedn = $r->dir_config('AuthenBaseDN');

  $requirement = REQUIRE_OPTS->{lc($requirement)} || 1;
  $r->log->debug(join ", ", "AuthzBaseDN=$basedn",
		 "GroupAttrType=$groupattrtype",
		 "LDAPServer=$authzldapserver",
		 "MemberAttrType=$memberattrtype",
		 "MemberAttrValue=$memberattrvalue",
		 "NestedAttrType=$nestedattrtype",
		 "NestedGroups=$nested_groups",
		 "Requirement=$requirement",
		 "UserBaseDN=$userbasedn");

  for my $req (@$requires) {
    my ($require, $rest) = split /\s+/, $req->{requirement}, 2;

    if ($require eq "user") { return OK
				if grep $username eq $_, split /\s+/, $rest}
    elsif ($require eq "valid-user") { return OK }
    elsif ($require eq 'group') {
      my $ld = undef;
      # Connect to the server
      unless ($ld = new Net::LDAP($authenldapserver,port => $authenldapport)) {
	$r->note_basic_auth_failure;
	$r->log_reason("user $username: Authen 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 $username: Authen LDAP Initial Bind Failed: " .
		       $msg->code . " " . $msg->error, $r->uri);
	return SERVER_ERROR;
      }

      # Get user DN
      $msg = $ld->search(base   => $userbasedn,
			 filter => qq($uidattrtype=$username));
      unless ($msg->code == LDAP_SUCCESS) {
	$r->note_basic_auth_failure;
	$r->log_reason("LDAP read failure " .
		       $msg->code . " " . $msg->error, $r->uri);
	return SERVER_ERROR;
      }
      unless ($msg->count) {
	$r->note_basic_auth_failure;
	$r->log_reason("user ($uidattrtype) $username doesn't " .
		       "exist in LDAP " .
		       $msg->code . " " . $msg->error . $r->uri);
	return AUTH_REQUIRED;
      }

      my $userinfo = undef;
      if ($memberattrvalue eq 'dn') {
	$userinfo = $msg->first_entry->dn;
      } else {
	$userinfo = ($msg->first_entry->get($memberattrvalue))[0];	
      }
      $r->log->debug("handler: Userinfo is $userinfo ($memberattrvalue)");

      $ld->unbind();
      $ld = undef;
      # Connect to the server
      unless ($ld = new Net::LDAP($authzldapserver,port => $authzldapport)) {
	$r->note_basic_auth_failure;
	$r->log_reason("user $username: Authz LDAP Connection Failed",$r->uri);
	return SERVER_ERROR;
      }

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

      # Compare the username
      my ($result, $group) = check_group($r, $ld, $basedn, $groupattrtype,
					 $memberattrtype, $userinfo, $rest,
					 $nestedattrtype, $nested_groups,
					 $requirement, 1);
      return $result unless $result == OK;

      # Everything's A-OK
      $r->log->debug("Setting REMOTE_GROUP to $group");
      $r->subprocess_env(REMOTE_GROUP => $group);
      $r->headers_in->{'REMOTE_GROUP'} = $group;
      return OK;
    }
  }
}

1;

__END__

# Documentation - try 'pod2text AuthzLDAP'

=head1 NAME

Apache::AuthzLDAP - mod_perl LDAP Authorization Module

=head1 SYNOPSIS

 <Directory /foo/bar>
 # Authorization 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 AuthzBaseDN          o=My Company      # Default: none
 PerlSetVar AuthzGroupAttrType   gid               # Default: cn
 PerlSetVar AuthzLDAPServer      ldap.foo.com      # Default: localhost
 PerlSetVar AuthzLDAPPort        389               # Default: 389
 PerlSetVar AuthzMemberAttrType  uniquemember      # Default: member
 PerlSetVar AuthzMemberAttrValue dn                # Default: cn
 PerlSetVar AuthzNestedAttrType  uniquegroup       # Default: member
 PerlSetVar AuthzNestedGroups    on                # Default: off
 PerlSetVar AuthzRequire         inAllGroups       # Default: inAGroup
 PerlSetVar AuthzUidattrType     userid            # Default: uid

 PerlAuthzHandler Apache::AuthzLDAP

 require group "My Group" GroupA "Group B"         # Authorize user against
                                                   # multiple groups
 </Directory>



( run in 2.716 seconds using v1.01-cache-2.11-cpan-2398b32b56e )