Commit a00d0d25 authored by Yadd's avatar Yadd
Browse files

Add AD [Not tested !] (#595)

parent e7a712c0
package Lemonldap::NG::Portal::Auth::AD;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants;
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Auth::LDAP';
has adPwdMaxAge => (
is => 'rw',
builder => sub {
my $conf = $_[0]->{conf};
my $res = $conf->{ADPwdMaxAge} || 0;
return $res * 10000000; # padding with '0' to obtain 0.1 micro-seconds
}
);
has adPwdExpireWarning => (
is => 'rw',
builder => sub {
my $conf = $_[0]->{conf};
my $res = $conf->{ADPwdExpireWarning} || 0;
return $res * 10000000; # padding with '0' to obtain 0.1 micro-seconds
}
);
sub init {
my ($self) = @_;
$self->conf->{ldapExportedVars}->{_AD_pwdLastSet} = 'pwdLastSet';
$self->conf->{ldapExportedVars}->{_AD_userAccountControl} =
'userAccountControl';
$self->conf->{ldapExportedVars}->{_AD_msDS_UACC} =
'msDS-User-Account-Control-Computed';
if ( $self->adPwdExpireWarning > $self->adPwdMaxAge ) {
$self->adPwdExpireWarning( $self->adPwdMaxAge );
$self->lmLog(
"Error: ADPwdExpireWarning > ADPwdMaxAge, this should not happen",
'warn' );
}
return $self->SUPER::init();
}
sub authenticate {
my ( $self, $req ) = @_;
my $res = $self->SUPER::authenticate;
unless ( $res == PE_OK ) {
# Check specific AD attributes
my $pls = $req->{sessionInfo}->{_AD_pwdLastSet};
my $computed = $req->{sessionInfo}->{_AD_msDS_UACC};
my $mask = 0xf00000; # mask to get the 8 at 6th position
my $expired_flag =
0x800000; # 8 at 6th position for flag UF_PASSWORD_EXPIRED to be set
if ( ( $computed & $mask ) == $expired_flag ) {
$self->lmLog( "[AD] Password has expired", 'warn' );
$res = PE_PP_PASSWORD_EXPIRED;
}
# Password must be changed if pwdLastSet 0
if ( defined $pls and $pls == 0 ) {
$self->lmLog( "[AD] Password reset. User must change his password",
'warn' );
$res = PE_PP_CHANGE_AFTER_RESET;
}
}
else {
# get userAccountControl to ckeck password expiration flags
my $_adUac = $req->{sessionInfo}->{_AD_userAccountControl} || 0;
# Compute current timestamp in AD format (date)
my $time = time; # unix timestamp (seconds since Jan 01 1970)
my $a_time =
$time + 11644473600; # adding difference (in s) from Jan 01 1601
my $timestamp =
$a_time . '0000000'; # padding with '0' to obatin 0.1 micro-seconds
# Compute password expiration time (date)
my $_pwdExpire = $req->{sessionInfo}->{_AD_pwdLastSet} || $timestamp;
$_pwdExpire += $self->adPwdMaxAge;
# computing when the warning message is displayed on portal
# (date - delay = date)
my $_pwdWarning = $_pwdExpire - $self->adPwdExpireWarning;
# display warning if account warning time before expiration is
# reached and flag "password nevers expires" is not set
if ( $timestamp > $_pwdWarning
&& $timestamp < $_pwdExpire
&& ( $_adUac & 0x10000 ) != 0x10000 )
{
# calculating remaining time before password expiration
my $remainingTime = $_pwdExpire - $timestamp;
$self->info(
"<h3>"
. sprintf(
$self->msg(PM_PP_EXP_WARNING),
$self->convertSec(
substr( $remainingTime, 0, length($remainingTime) - 7 )
)
)
. "</h3>"
);
}
}
# Remember password if password reset needed
$req->datas->{oldpassword} = $req->datas->{password}
if (
$res == PE_PP_CHANGE_AFTER_RESET
or ( $res == PE_PP_PASSWORD_EXPIRED
and $self->conf->{ldapAllowResetExpiredPassword} )
);
return $res;
}
1;
package Lemonldap::NG::Portal::UserDB::AD;
use strict;
use Mouse;
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::UserDB::LDAP';
has filter => (
is => 'rw',
lazy => 1,
builder => sub {
$_[0]->{conf}->{AuthLDAPFilter} ||=
'(&(sAMAccountName=$user)(objectClass=person))';
$_[0]->{conf}->{mailLDAPFilter} ||=
'(&(mail=$mail)(objectClass=person))';
return $_[0]->buildFilter;
}
);
1;
......@@ -74,27 +74,28 @@ has attrs => (
has filter => (
is => 'rw',
lazy => 1,
builder => sub {
my $conf = $_[0]->{conf};
$_[0]->{p}
->lmLog( "LDAP Search base: $_[0]->{conf}->{ldapBase}", 'debug' );
# TODO : mailLDAPFilter
my $filter =
$conf->{AuthLDAPFilter}
|| $conf->{LDAPFilter}
|| '(&(uid=$user)(objectClass=inetOrgPerson))';
$filter =~ s/"/\\"/g;
$filter =~ s/\$(\w+)/".\$req->{sessionInfo}->{$1}."/g;
$filter =~ s/\$req->\{sessionInfo\}->\{user\}/\$req->{user}/g;
$filter =~
s/\$req->\{sessionInfo\}->\{(_?password|mail)\}/\$req->{datas}->{$1}/g;
$_[0]->{p}->lmLog( "LDAP transformed filter: $filter", 'debug' );
$filter = "sub{my(\$req)=\$_[0];return \"$filter\";}";
return eval $filter;
}
builder => 'buildFilter',
);
sub buildFilter {
my $conf = $_[0]->{conf};
$_[0]->{p}->lmLog( "LDAP Search base: $_[0]->{conf}->{ldapBase}", 'debug' );
# TODO : mailLDAPFilter
my $filter =
$conf->{AuthLDAPFilter}
|| $conf->{LDAPFilter}
|| '(&(uid=$user)(objectClass=inetOrgPerson))';
$filter =~ s/"/\\"/g;
$filter =~ s/\$(\w+)/".\$req->{sessionInfo}->{$1}."/g;
$filter =~ s/\$req->\{sessionInfo\}->\{user\}/\$req->{user}/g;
$filter =~
s/\$req->\{sessionInfo\}->\{(_?password|mail)\}/\$req->{datas}->{$1}/g;
$_[0]->{p}->lmLog( "LDAP transformed filter: $filter", 'debug' );
$filter = "sub{my(\$req)=\$_[0];return \"$filter\";}";
return eval $filter;
}
sub init {
my ($self) = @_;
$self->ldap and $self->filter;
......
use Test::More;
use strict;
use IO::String;
require 't/test-lib.pm';
my $res;
SKIP: {
skip 'No LDAP server given', 3 unless ( $ENV{LDAPSERVER} );
init(
{
logLevel => 'debug',
useSafeJail => 1,
authentication => 'AD',
userDB => 'AD',
LDAPFilter => $ENV{LDAPFILTER} || '(cn=$user)',
ldapServer => $ENV{LDAPSERVER},
ldapBase => $ENV{LDAPBASE},
managerDn => $ENV{MANAGERDN} || '',
managerPassword => $ENV{MANAGERPASSWORD} || '',
}
);
my $postString = 'user='
. ( $ENV{LDAPACCOUNT} || 'dwho' )
. '&password='
. ( $ENV{LDAPPWD} || 'dwho' );
# Try yo authenticate
# -------------------
ok(
$res = &client->_post(
'/', '',
IO::String->new($postString),
'application/x-www-form-urlencoded',
length($postString)
),
'Auth query'
);
ok( $res->[0] == 200, 'Response is 200' ) or explain( $res->[0], 200 );
my $cookies = getCookies($res);
my $id;
ok( $id = $cookies->{lemonldap}, 'Get cookie' )
or explain( $res, 'Set-Cookie: something' );
clean_sessions();
}
count(3);
done_testing( count() );
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment