lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/AD.pm
2022-02-03 11:20:47 +01:00

178 lines
5.0 KiB
Perl

# Auth::AD inherits from Auth::LDAP. It defines some additional configuration
# parameters and manage AD password expiration
package Lemonldap::NG::Portal::Auth::AD;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_PP_PASSWORD_EXPIRED
PE_PP_CHANGE_AFTER_RESET
PE_BADCREDENTIALS
);
our $VERSION = '2.0.14';
extends 'Lemonldap::NG::Portal::Auth::LDAP';
# PROPERTIES
has adPwdMaxAge => (
is => 'rw',
lazy => 1,
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',
lazy => 1,
builder => sub {
my $conf = $_[0]->{conf};
my $res = $conf->{ADPwdExpireWarning} || 0;
return $res * 10000000; # padding with '0' to obtain 0.1 micro-seconds
}
);
# AD timestamp starts from Jan 01 1601 and is defined in 0.1 micro seconds.
# This method converts Unix timestamp into AD timestamp.
sub adTime {
return ( time + 11644473600 ) * 10000000;
}
# INITIALIZATION
# update LDAP configuration
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->logger->warn(
"Error: ADPwdExpireWarning > ADPwdMaxAge, this should not happen",
);
}
return $self->SUPER::init();
}
# RUNNING METHODS
sub authenticate {
my ( $self, $req ) = @_;
my $res = $self->SUPER::authenticate($req);
my $pls =
$self->ldap->getLdapValue( $req->data->{ldapentry}, 'pwdLastSet' );
my $computed = $self->ldap->getLdapValue( $req->data->{ldapentry},
'msDS-User-Account-Control-Computed' );
my $_adUac =
$self->ldap->getLdapValue( $req->data->{ldapentry}, 'userAccountControl' )
|| 0;
unless ( $res == PE_OK ) {
# Explicit bad credentials message
if ( $req->data->{ldapError}
and $req->data->{ldapError} =~ /LdapErr: .* data ([^,]+),.*/ )
{
if ( $1 eq '52e' ) {
return PE_BADCREDENTIALS;
}
}
# Check specific AD attributes
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->userLogger->warn("[AD] Password has expired");
$res = PE_PP_PASSWORD_EXPIRED;
}
# Password must be changed if pwdLastSet 0
if ( defined $pls and $pls == 0 ) {
$self->userLogger->warn(
"[AD] Password reset. User must change his password");
$res = PE_PP_CHANGE_AFTER_RESET;
}
}
else {
my $timestamp = $self->adTime;
# Compute password expiration time (date)
my $_pwdExpire = $pls || $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;
$req->info(
$self->loadTemplate(
$req,
'pwdWillExpire',
params => {
time => join(
',',
$self->ldap->convertSec(
substr(
$remainingTime, 0,
length($remainingTime) - 7
)
)
)
}
)
);
}
}
# Remember password if password reset needed
if (
$res == PE_PP_CHANGE_AFTER_RESET
or ( $res == PE_PP_PASSWORD_EXPIRED
and $self->conf->{ldapAllowResetExpiredPassword} )
)
{
$req->data->{oldpassword} = $req->data->{password}; # Fix 2377
$req->data->{noerror} = 1;
$self->setSecurity($req);
}
return $res;
}
# Define which error codes will stop Combination process
# @param res error code
# @return result 1 if stop is needed
sub stop {
my ( $self, $res ) = @_;
return 1
if ( $res == PE_PP_PASSWORD_EXPIRED
or $res == PE_PP_CHANGE_AFTER_RESET );
return 0;
}
1;