Apache2-AuthZSympa
view release on metacpan or search on metacpan
lib/Apache2/AuthZSympa.pm view on Meta::CPAN
## if CAS uid is an email address, no need these directives
PerlSetVar LDAPHost ldap.localdomain
PerlSetVar LDAPSuffix ou=people
PerlSetVar LDAPEmailFilter (uid=[uid])
PerlSetVar LDAPEmailAttribute mail
PerlSetVar LDAPScope sub
PerlAuthzHandler Apache2::AuthZSympa
require valid-user
require SympaLists sympa-users@demo.sympa.org,sympa-test@demo.sympa.org
</Directory>
=head1 SHIBBOLETH AUTHENTICATION
Shibboleth is an open source software developped by Internet2 : http://shibboleth.internet2.edu
The default behavior of mod_shib authentication module is to provide the user email address in the HTTP_SHIB_INETORGPERSON_MAIL HTTP header. The AuthZSympa module still provides a ShibbolethMailVar parameter to declare which HTTP header contains the...
The following rules are required:
=over
=item *
AuthType shibboleth
=item *
require valid-user
=item *
ShibbolethMailVar (if not HTTP_SHIB_INETORGPERSON_MAIL)
=back
Example:
<Directory "/var/www/somewhere">
AuthType shibboleth
PerlSetVar SympaSoapServer http://mysympa.server/soap
PerlSetVar MemcachedServer 10.219.213.24:11211
PerlSetVar CacheExptime 3600 # in seconds, default 1800
PerlSetVar ShibbolethMailVar HTTP_SHIB_INETORGPERSON_MAIL
PerlAuthzHandler Apache2::AuthZSympa
require valid-user
require SympaLists sympa-users@demo.sympa.org,sympa-test@demo.sympa.org
</Directory>
=head1 COMPLETE MODULE RULES LIST
# required to identify the good authentication type
AuthType CAS # can be SSL, Sympa or shibboleth
# URL to query Sympa server SOAP interface, required
PerlSetEnv SympaSoapServer
# lists to verify membership of user, required
require SympaLists list1@mydomain,list2@mydomain
# IP address and port of memcached server if necessary
PerlSetEnv MemcachedServer 192.168.0.1:11211
# Cache expiration time in seconds if memcached server used, default 1800
PerlSetEnv CacheExptime 3600
# LDAP Host for CAS backend
PerlSetEnv LDAPHost ldap.mydomain
# LDAP suffix to query LDAP backend
PerlSetenv LDAPSuffix o=people
# Filter to query LDAP backend. It has to match uid provided by CAS server
PerlSetenv LDAPEmailFilter myIdAttribute=([uid])
# LDAP backend attribute containing email address
PerlSetenv LDAPEmailAttribute mail
# LDAP scope, default sub
PerlSetenv LDAPScope sub
# Shibboleth env var to match email address. optional, default HTTP_SHIB_INETORGPERSON_MAIL
PerlSetenv ShibbolethMailVar HTTP_SHIB_INETORGPERSON_MAIL
=cut
sub handler{
my $r= shift;
return Apache2::Const::OK unless $r->is_initial_req;
## Location Variables to connect to the good server
my $SympaSoapServer = $r->dir_config('SympaSoapServer') || "localhost"; ## url of sympa soap server
my $cacheserver = $r->dir_config('MemcachedServer') || "127.0.0.1:11211"; ## cache server
my $exptime = $r->dir_config('CacheExptime') || 1800; ## 30 minutes of cache
my $ShibMailVar = $r->dir_config('ShibbolethMailVar') || 'HTTP_SHIB_INETORGPERSON_MAIL';
my $SympaList = ""; ## list for which the mail will be checked
my $mail_user;
my $response;
my $result;
my $auth_type = lc($r->auth_type);
my $requires = $r->requires;
my $location = $r->location;
# verify if require SympaLists is present
for my $entry (@$requires){
my $requirement;
if ($entry->{requirement} =~ /SympaLists/){
($requirement,$SympaList) = split(/\s+/,$entry->{requirement});
$r->log->debug("Apache2::AuthZSympa : require type '$requirement' for $location with lists $SympaList");
last;
}
}
my @SympaLists = split(/\,/,$SympaList);
## instanciation of a new Soap::Lite object
my $soap;
my $soap_error=0;
my $soap_session;
my $soap_res;
unless($soap = new SOAP::Lite()){
$r->log_error("Apache2::AuthZSympa : Unable to create SOAP::Lite object while accessing $location");
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
## if there is an error during soap request. $soap_error will be instanciated
$soap->on_fault(sub {
($soap_session, $soap_res) = @_;
$soap_error=1;
});
$soap->uri('urn:sympasoap');
$soap->proxy($SympaSoapServer);
unless(defined $soap){
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
## instanciation of cache
## preventing from errors, verification of its naming
unless( $cacheserver =~ /[^\:]+\:\d{1,6}/){
$r->log_error("Apache2::AuthZSympa configuration ($location) : memcached server ($cacheserver) naming format is incorrect, a port number is required");
return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
}
my $cache = new Cache::Memcached {
'servers' => [ $cacheserver ],
'namespace' => 'AuthZSympa',
};
## if an email from SSL request is got, then authentication was made via SSL
$r->subprocess_env;
my $subr = $r->lookup_uri($r->uri);
my $ssl_proto = $subr->subprocess_env('SSL_CLIENT_S_DN_Email');
if ($subr->subprocess_env('SSL_CLIENT_S_DN_Email') && ($auth_type eq "ssl")){
$mail_user=$subr->subprocess_env('SSL_CLIENT_S_DN_Email');
$r->user($mail_user);
}elsif($auth_type eq 'basic' && $r->user){
## if basic_auth, get remote_user
$mail_user= $r->user;
}elsif($auth_type eq 'cas'){
## if CAS
my $user = $r->user;
$mail_user = "";
## verification of ldap directives
my $ldap_host = $r->dir_config('LDAPHost') || "";
if ($ldap_host eq ""){
$r->log->debug("Apache2::AuthZSympa : no LDAPHost, email adress in uid ?");
if ($user =~ /@/){
## if user is emailAddress, don't need ldap to retrieve emailadddress
$r->log->debug("Apache2::AuthZSympa : no need with LDAP, email adress in uid");
$mail_user = $user;
}else{
$r->log_error("Apache2::AuthZSympa : no ldap_host defined for $location, can't verify registrations");
return Apache2::Const::HTTP_UNAUTHORIZED;
}
}
## key for cache (key for email)
my $user_key = md5_hex($r->user.$ldap_host);
## verification first in cache
if (defined $cache->get($user_key)){
$r->log->debug("Apache2::AuthZSympa : retrieve mail from cache for $user_key");
$mail_user = ${$cache->get($user_key)};
$r->log->debug("Apache2::AuthZSympa : retrieved mail ($mail_user) from cache") if $mail_user ne "";
}
## then retrieve mail from ldap
if ($mail_user eq ""){
$r->log->debug("Apache2::AuthZSympa : retrieve mail from LDAP");
$mail_user = &casGetMail($r);
}
if ($mail_user ne ""){
$r->log->debug("Apache2::AuthZSympa : retrieved mail $mail_user");
$cache->set($user_key,\$mail_user,$exptime);
}else{
$r->log_error("Apache2::AuthZSympa : no mail for $user in $ldap_host");
return Apache2::Const::HTTP_UNAUTHORIZED;
}
}elsif($auth_type eq 'shibboleth'){
$mail_user=$ENV{$ShibMailVar};
if($mail_user eq ""){
$r->log_error("Apache2::AuthZSympa : no mail in var $ShibMailVar");
$r->log->debug("Apache2::AuthZSympa : $ShibMailVar value : $mail_user");
return Apache2::Const::HTTP_UNAUTHORIZED;
}else{
$r->log->debug("Apache2::AuthZSympa : $ShibMailVar value : $mail_user");
}
}else{
$r->log_error("Apache2::AuthZSympa : no user authenticated for $location, can't verify registrations");
return Apache2::Const::HTTP_UNAUTHORIZED;
}
## key generation for cache : md5($mail_user + server name) -> prevents from errors when updating
my $user_key = md5_hex($mail_user.$SympaSoapServer);
## verify subscription first in cache
## if its in the cache as OK for the list, go,
## if its in all the list as not OK, refuse
## else, next step
my %cache_lists;
if (defined $cache){
if (defined $cache->get($user_key)){
%cache_lists = %{$cache->get($user_key)};
}
my $ok=1;
foreach my $list (@SympaLists){
if (defined $cache_lists{$list}){
if ($cache_lists{$list} == 1){
return Apache2::Const::OK;
}elsif($cache_lists{$list} == 0){
$ok = 0;
}
}
}
if ($ok == 0){
my $lists_string = join(", nor in ",@SympaLists);
$r->log->notice("Apache2::AuthZSympa : $location. $mail_user is not registred on server $SympaSoapServer in ",$lists_string);
return Apache2::Const::HTTP_UNAUTHORIZED;
}
}
## if not in cache, verify soap server
foreach my $list (@SympaLists){
$r->log->debug("Apache2::AuthZSympa liste $list");
$soap_error=0;
$list =~ s/\s//g;
$response = $soap->amI($list,'subscriber',$mail_user);
## verify if error during soap service request
if ($soap_error==1){
my ($type_error,$detail) = &traite_soap_error($soap, $soap_res);
if ($type_error eq 'ERROR'){
$r->log_error("Apache2::AuthZSympa : $location, SOAP error $detail (server $SympaSoapServer)");
}else{
$r->log->notice("Apache2::AuthZSympa : $location, $detail (server $SympaSoapServer)");
};
$cache_lists{$list} = 0;
next;
}else{
$result = $response->result;
if ($result == 1){
if (defined $cache){
$cache_lists{$list} = 1;
$cache->set($user_key, \%cache_lists,$exptime);
}
return Apache2::Const::OK;
}else{
$cache_lists{$list} = 0;
}
}
}
$cache->set($user_key, \%cache_lists,$exptime);
my $lists_string = join(", nor in ",@SympaLists);
$r->log->notice("Apache2::AuthZSympa : $location. $mail_user is not registred on server $SympaSoapServer in ",$lists_string);
return Apache2::Const::HTTP_UNAUTHORIZED;
}
sub traite_soap_error {
my ($soap, $res) = @_;
my $detail = "";
my $type = "";
if(ref(\$res) eq 'REF'){
$detail = $res->faultdetail;
$type = "NOTICE";
}else{
$detail = $soap->transport->status;
$type = "ERROR";
};
return ($type, $detail);
}
sub casGetMail(){
my ($r) = @_;
my $error="";
use Net::LDAP;
my $user = $r->user;
my $ldap_host = $r->dir_config('LDAPHost');
my $ldap_suffix = $r->dir_config('LDAPSuffix');
my $uid_filter = $r->dir_config('LDAPEmailFilter');
my $attribute = $r->dir_config('LDAPEmailAttribute');
my $scope = $r->dir_config('LDAPScope') || "sub";
my $location = $r->location;
my $ldap;
unless($ldap = Net::LDAP->new($ldap_host)){
$r->log_error("Apache2::AuthZSympa : $location, unable to create Net::LDAP object with $ldap_host");
return "";
( run in 0.625 second using v1.01-cache-2.11-cpan-98e64b0badf )