Merge remote-tracking branch 'mamesene/certificateReset-1605'

This commit is contained in:
Xavier 2019-09-18 21:44:31 +02:00
commit fedea4abbd
46 changed files with 1633 additions and 44 deletions

View File

@ -18,16 +18,24 @@ sub defaultValues {
'authChoiceParam' => 'lmAuth',
'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionTempo' => 30,
'captcha_mail_enabled' => 1,
'captcha_register_enabled' => 1,
'captcha_size' => 6,
'casAccessControlPolicy' => 'none',
'casAuthnLevel' => 1,
'checkTime' => 600,
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionTempo' => 30,
'captcha_mail_enabled' => 1,
'captcha_register_enabled' => 1,
'captcha_size' => 6,
'casAccessControlPolicy' => 'none',
'casAuthnLevel' => 1,
'certificateResetByMailCeaAttribute' => 'description',
'certificateResetByMailCertificateAttribute' =>
'userCertificate;binary',
'certificateResetByMailReplyTo' => 'noreply@example.com',
'certificateResetByMailSender' => 'noreply@example.com',
'certificateResetByMailURL' =>
'http://auth.example.com/certificateReset',
'certificateResetByMailValidityDelay' => 0,
'checkTime' => 600,
'checkUserHiddenAttributes' => '_loginHistory _session_id hGroups',
'checkUserIdRule' => 1,
'checkXSS' => 1,
@ -210,6 +218,7 @@ sub defaultValues {
'portalAntiFrame' => 1,
'portalCheckLogins' => 1,
'portalDisplayAppslist' => 1,
'portalDisplayCertificateResetByMail' => 0,
'portalDisplayChangePassword' => '$_auth =~ /^(LDAP|DBI|Demo)$/',
'portalDisplayFavApps' => 1,
'portalDisplayGeneratePassword' => 1,

View File

@ -19,6 +19,7 @@ lib/Lemonldap/NG/Handler/Lib/PSGI.pm
lib/Lemonldap/NG/Handler/Lib/SecureToken.pm
lib/Lemonldap/NG/Handler/Lib/ServiceToken.pm
lib/Lemonldap/NG/Handler/Lib/Status.pm
lib/Lemonldap/NG/Handler/Lib/StatusConstants.pm
lib/Lemonldap/NG/Handler/Lib/ZimbraPreAuth.pm
lib/Lemonldap/NG/Handler/Main.pm
lib/Lemonldap/NG/Handler/Main/Init.pm

View File

@ -97,7 +97,10 @@ sub portalConsts {
'91' => 'PE_OID_SERVICE_NOT_ALLOWED',
'92' => 'PE_GET_SERVICE_NOT_ALLOWED',
'93' => 'PE_IMPERSONATION_SERVICE_NOT_ALLOWED',
'94' => 'PE_ISSUERMISSINGREQATTR'
'94' => 'PE_ISSUERMISSINGREQATTR',
'95' => 'PE_RESETCERTIFICATE_INVALIDE',
'96' => 'PE_RESETCERTIFICATE_FOREMPTY',
'97' => 'PE_RESETCERTIFICATE_FIRSTACCESS'
};
}

View File

@ -754,6 +754,42 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 0,
'type' => 'bool'
},
'certificateResetByMailCeaAttribute' => {
'default' => 'description',
'type' => 'text'
},
'certificateResetByMailCertificateAttribute' => {
'default' => 'userCertificate;binary',
'type' => 'text'
},
'certificateResetByMailReplyTo' => {
'default' => 'noreply@example.com',
'type' => 'text'
},
'certificateResetByMailSender' => {
'default' => 'noreply@example.com',
'type' => 'text'
},
'certificateResetByMailStep1Body' => {
'type' => 'longtext'
},
'certificateResetByMailStep1Subject' => {
'type' => 'text'
},
'certificateResetByMailStep2Body' => {
'type' => 'longtext'
},
'certificateResetByMailStep2Subject' => {
'type' => 'text'
},
'certificateResetByMailURL' => {
'default' => 'http://auth.example.com/certificateReset',
'type' => 'url'
},
'certificateResetByMailValidityDelay' => {
'default' => 0,
'type' => 'int'
},
'cfgAuthor' => {
'type' => 'text'
},
@ -2327,6 +2363,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 1,
'type' => 'boolOrExpr'
},
'portalDisplayCertificateResetByMail' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'portalDisplayChangePassword' => {
'default' => '$_auth =~ /^(LDAP|DBI|Demo)$/',
'type' => 'boolOrExpr'

View File

@ -963,6 +963,11 @@ sub attributes {
type => 'boolOrExpr',
documentation => 'Display logout tab in portal',
},
portalDisplayCertificateResetByMail => {
type => 'boolOrExpr',
default => 0,
documentation => 'Display Certificate Reset by mail tab in portal',
},
portalDisplayRegister => {
default => 1,
type => 'bool',
@ -1420,6 +1425,53 @@ sub attributes {
default => 'http://auth.example.com/resetpwd',
documentation => 'URL of password reset page',
},
# Certificate reset by mail
certificateResetByMailCeaAttribute => {
type => 'text',
default => 'description'
},
certificateResetByMailCertificateAttribute => {
type => 'text',
default => 'userCertificate;binary',
},
certificateResetByMailStep1Body => {
type => 'longtext',
documentation => 'Custom Certificate reset mail body',
},
certificateResetByMailStep2Body => {
type => 'longtext',
documentation => 'Custom confirm Certificate reset mail body',
},
certificateResetByMailStep2Subject => {
type => 'text',
documentation => 'Mail subject for reset confirmation',
},
certificateResetByMailStep1Subject => {
type => 'text',
documentation => 'Mail subject for certificate reset email',
},
certificateResetByMailURL => {
type => 'url',
default => 'http://auth.example.com/certificateReset',
documentation => 'URL of certificate reset page',
},
certificateResetByMailSender => {
type => 'text',
default => 'noreply@example.com',
documentation => 'URL of certificate reset page',
},
certificateResetByMailReplyTo => {
type => 'text',
default => 'noreply@example.com',
documentation => 'URL of certificate reset page',
},
certificateResetByMailValidityDelay => {
type => 'int',
default => 0
},
# Registration
registerConfirmSubject => {

View File

@ -102,7 +102,9 @@ sub portalConstants {
PE_GET_SERVICE_NOT_ALLOWED => 92,
PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93,
PE_ISSUERMISSINGREQATTR => 94,
PE_RESETCERTIFICATE_INVALIDE => 95,
PE_RESETCERTIFICATE_FOREMPTY => 96,
PE_RESETCERTIFICATE_FIRSTACCESS => 97,
};
}

View File

@ -74,7 +74,8 @@ sub tree {
'portalCheckLogins',
'portalDisplayResetPassword',
'passwordResetAllowedRetries',
'portalDisplayRegister'
'portalDisplayRegister',
'portalDisplayCertificateResetByMail',
]
},
{
@ -666,6 +667,34 @@ sub tree {
]
}
]
},
{
title => 'certificateResetByMailManagement',
form => 'simpleInputContainer',
nodes => [ {
title => 'certifcateMailContent',
form => 'simpleInputContainer',
nodes => [
'certificateResetByMailSender',
'certificateResetByMailReplyTo',
'certificateResetByMailStep1Subject',
'certificateResetByMailStep1Body',
'certificateResetByMailStep2Subject',
'certificateResetByMailStep2Body'
]
},
{
title => 'mailOther',
form => 'simpleInputContainer',
nodes => [
'certificateResetByMailURL',
'certificateResetByMailCeaAttribute',
'certificateResetByMailCertificateAttribute',
'certificateResetByMailValidityDelay'
]
}
]
},
{
title => 'register',

View File

@ -141,7 +141,20 @@
"casStorageOptions":" خيارات وحدة جلسات كاس",
"categoryName":"اسم الفئة",
"cda":"نطاقات متعددة",
"contentSecurityPolicy":"السياسة الأمنية للمحتوى",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Content security policy",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Hidden attributes",
"contextSwitchingIdRule":"Identities use rule",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"CAS sessions module options",
"categoryName":"Category name",
"cda":"Mehrere Domains",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Content security policy",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Hidden attributes",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"CAS sessions module options",
"categoryName":"Category name",
"cda":"Multiple domains",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Content security policy",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Hidden attributes",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"Options du module des sessions CAS",
"categoryName":"Nom de la catégorie",
"cda":"Domaines multiples",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Politique de sécurité de contenu",
"contextSwitching":"Endossement d'identité",
"contextSwitchingHiddenAttributes":"Attributs masqués",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"Opzioni del modulo sessioni CAS",
"categoryName":"Nome della categoria",
"cda":"Domini multipli",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Politica di protezione dei contenuti",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Attributi nascosti",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"Các tùy chọn mô-đun phiên CAS",
"categoryName":"Tên thể loại",
"cda":"Nhiều tên miền",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Chính sách bảo mật nội dung",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Hidden attributes",

View File

@ -141,6 +141,19 @@
"casStorageOptions":"CAS 会话模块选项",
"categoryName":"分类名称",
"cda":"Multiple domains",
"certifcateMailContent":"Certificate mail content",
"certificateResetByMailManagement":"Certifcate reset management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
"certificateResetByMailCertificateAttribute":"certificate attribute name",
"certificateResetByMailSender":"Mail sender",
"certificateResetByMailReplyTo":"Reply address",
"certificateResetByMailStep1Subject":"Certificate reset mail subject",
"certificateResetByMailStep1Body":"Certificate reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your Certificate",
"contentSecurityPolicy":"Content security policy",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Hidden attributes",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -47,6 +47,7 @@ lib/Lemonldap/NG/Portal/Auth/SSL.pm
lib/Lemonldap/NG/Portal/Auth/Twitter.pm
lib/Lemonldap/NG/Portal/Auth/WebID.pm
lib/Lemonldap/NG/Portal/CDC.pm
lib/Lemonldap/NG/Portal/CertificateResetByMail/LDAP.pm
lib/Lemonldap/NG/Portal/Issuer/CAS.pm
lib/Lemonldap/NG/Portal/Issuer/Get.pm
lib/Lemonldap/NG/Portal/Issuer/OpenID.pm
@ -101,6 +102,7 @@ lib/Lemonldap/NG/Portal/Password/REST.pm
lib/Lemonldap/NG/Portal/Plugins/AutoSignin.pm
lib/Lemonldap/NG/Portal/Plugins/BruteForceProtection.pm
lib/Lemonldap/NG/Portal/Plugins/CDA.pm
lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm
lib/Lemonldap/NG/Portal/Plugins/CheckState.pm
lib/Lemonldap/NG/Portal/Plugins/CheckUser.pm
lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm
@ -364,6 +366,7 @@ site/htdocs/static/languages/zh.json
site/templates/bootstrap/2fchoice.tpl
site/templates/bootstrap/2fregisters.tpl
site/templates/bootstrap/casBack2Url.tpl
site/templates/bootstrap/certificateReset.tpl
site/templates/bootstrap/checklogins.tpl
site/templates/bootstrap/checkuser.tpl
site/templates/bootstrap/choice.tpl
@ -431,6 +434,7 @@ site/templates/common/mail/it.json
site/templates/common/mail/vi.json
site/templates/common/mail/zh_CN.json
site/templates/common/mail_2fcode.tpl
site/templates/common/mail_certificatReset.tpl
site/templates/common/mail_confirm.tpl
site/templates/common/mail_footer.tpl
site/templates/common/mail_header.tpl
@ -563,6 +567,7 @@ t/43-MailPasswordReset-LDAP.t
t/43-MailPasswordReset-with-captcha.t
t/43-MailPasswordReset-with-token.t
t/43-MailPasswordReset.t
t/44-CertificateResetByMail-LDAP.t
t/50-IssuerGet.t
t/59-Double-cookies-for-a-Single-session.t
t/59-Double-cookies-for-Double-sessions.t

View File

@ -0,0 +1,72 @@
package Lemonldap::NG::Portal::CertificateResetByMail::LDAP;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_LDAPCONNECTFAILED
PE_LDAPERROR
PE_OK
PE_ERROR
);
extends 'Lemonldap::NG::Portal::Lib::LDAP';
our $VERSION = '2.0.0';
# RUNNING METHODS
# PRIVATE METHODS
sub modifCertificate {
my $self = shift;
my $newcertif = shift;
my $usercertif = shift;
my $req = shift;
my $ceaAttribute = $self->conf->{certificateResetByMailCeaAttribute}
|| "description";
my $certificateAttribute =
$self->conf->{certificateResetByMailCertificateAttribute}
|| "userCertificate;binary";
# Set the dn unless done before
#
#
my $dn;
if ( $req->userData->{_dn} ) {
$dn = $req->userData->{_dn};
$self->logger->debug("Get DN from request data: $dn");
}
else {
$dn = $req->sessionInfo->{_dn};
$self->logger->debug("Get DN from session data: $dn");
}
unless ($dn) {
$self->logger->error('"dn" is not set, aborting password modification');
return PE_ERROR;
}
#my $dn = "uid=" . $req->{user}. "," . $self->conf->{ldapBase};
my $result = $self->ldap->modify(
$dn,
replace => [
$ceaAttribute => $newcertif,
"$certificateAttribute" => [$usercertif]
]
);
unless ( $result->code == 0 ) {
$self->logger->debug( "LDAP modify Error: " . $result->code );
$self->ldap->unbind;
$self->{flags}->{ldapActive} = 0;
$self->ldap->unbind;
return PE_LDAPERROR;
}
$self->logger->debug("$ceaAttribute set to $newcertif");
return PE_OK;
}
1;

View File

@ -98,6 +98,9 @@ use constant {
PE_GET_SERVICE_NOT_ALLOWED => 92,
PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93,
PE_ISSUERMISSINGREQATTR => 94,
PE_RESETCERTIFICATE_INVALIDE => 95,
PE_RESETCERTIFICATE_FOREMPTY => 96,
PE_RESETCERTIFICATE_FIRSTACCESS => 97,
};
sub portalConsts {
@ -191,7 +194,10 @@ sub portalConsts {
'91' => 'PE_OID_SERVICE_NOT_ALLOWED',
'92' => 'PE_GET_SERVICE_NOT_ALLOWED',
'93' => 'PE_IMPERSONATION_SERVICE_NOT_ALLOWED',
'94' => 'PE_ISSUERMISSINGREQATTR'
'94' => 'PE_ISSUERMISSINGREQATTR',
'95' => 'PE_RESETCERTIFICATE_INVALIDE',
'96' => 'PE_RESETCERTIFICATE_FOREMPTY',
'97' => 'PE_RESETCERTIFICATE_FIRSTACCESS'
};
}
@ -289,7 +295,10 @@ our @EXPORT_OK = (
'PE_OID_SERVICE_NOT_ALLOWED',
'PE_GET_SERVICE_NOT_ALLOWED',
'PE_IMPERSONATION_SERVICE_NOT_ALLOWED',
'PE_ISSUERMISSINGREQATTR'
'PE_ISSUERMISSINGREQATTR',
'PE_RESETCERTIFICATE_INVALIDE',
'PE_RESETCERTIFICATE_FOREMPTY',
'PE_RESETCERTIFICATE_FIRSTACCESS'
);
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK, 'import' ], );

View File

@ -307,6 +307,8 @@ sub display {
ASK_LOGINS => $req->param('checkLogins') || 0,
DISPLAY_RESETPASSWORD => $self->conf->{portalDisplayResetPassword},
DISPLAY_REGISTER => $self->conf->{portalDisplayRegister},
DISPLAY_UPDATECERTIF => $self->conf->{portalDisplayCertificateResetByMail},
MAILCERTIF_URL => $self->conf->{certificateResetByMailURL},
MAIL_URL => $self->conf->{mailUrl},
REGISTER_URL => $self->conf->{registerUrl},
HIDDEN_INPUTS => $self->buildHiddenForm($req),

View File

@ -74,6 +74,8 @@ sub enabledPlugins {
# Check if register is enabled
push @res, '::Plugins::Register'
if ( $conf->{registerDB} and $conf->{registerDB} ne 'Null' );
push @res, '::Plugins::CertificateResetByMail'
if ( $conf->{portalDisplayCertificateResetByMail});
# Check if custom plugins are required
# TODO: change this name

View File

@ -91,6 +91,7 @@ sub error_type {
PE_MAILOK, PE_LOGOUT_OK,
PE_MAILFIRSTACCESS, PE_PASSWORDFIRSTACCESS,
PE_MAILCONFIRMOK, PE_REGISTERFIRSTACCESS,
PE_RESETCERTIFICATE_FIRSTACCESS,
)
)
);
@ -107,7 +108,7 @@ sub error_type {
PE_CONFIRM, PE_MAILFORMEMPTY,
PE_MAILCONFIRMATION_ALREADY_SENT, PE_PASSWORDFORMEMPTY,
PE_CAPTCHAEMPTY, PE_REGISTERFORMEMPTY,
PE_PP_CHANGE_AFTER_RESET
PE_PP_CHANGE_AFTER_RESET, PE_RESETCERTIFICATE_FOREMPTY,
)
)
);

View File

@ -0,0 +1,794 @@
package Lemonldap::NG::Portal::Plugins::CertificateResetByMail;
use strict;
use Encode;
use Mouse;
use Net::SSLeay;
use DateTime::Format::RFC3339;
use Digest::SHA qw(sha256_hex);
use MIME::Base64;
use POSIX qw(strftime);
use Lemonldap::NG::Common::FormEncode;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_BADMAILTOKEN
PE_CAPTCHAEMPTY
PE_CAPTCHAERROR
PE_MAILCONFIRMATION_ALREADY_SENT
PE_MAILCONFIRMOK
PE_MAILERROR
PE_MAILFIRSTACCESS
PE_MAILFORMEMPTY
PE_MAILNOTFOUND
PE_MAILOK
PE_MALFORMEDUSER
PE_NOTOKEN
PE_OK
PE_PASSWORDFIRSTACCESS
PE_PASSWORDFORMEMPTY
PE_PASSWORD_OK
PE_TOKENEXPIRED
PE_USERNOTFOUND
PE_RESETCERTIFICATE_INVALIDE
PE_RESETCERTIFICATE_FOREMPTY
PE_RESETCERTIFICATE_FIRSTACCESS
);
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Plugin',
'Lemonldap::NG::Portal::Lib::SMTP', 'Lemonldap::NG::Portal::Lib::_tokenRule';
# PROPERTIES
# Mail timeout token generator
# Form timout token generator (used even if requireToken is not set)
has ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->conf->{formTimeout} );
return $ott;
}
);
# Sub module (Demo, LDAP,...)
has registerModule => ( is => 'rw' );
# Captcha generator
has captcha => ( is => 'rw' );
# certificate reset url
has certificateResetUrl => (
is => 'rw',
lazy => 1,
default => sub {
my $p = $_[0]->conf->{portal};
$p =~ s#/*$##;
return "$p/certificateReset";
}
);
# Mail timeout token generator
has mailott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->cache(0);
$ott->timeout( $_[0]->conf->{registerTimeout}
|| $_[0]->conf->{timeout} );
return $ott;
}
);
# INITIALIZATION
sub init {
my ($self) = @_;
# Declare REST route
$self->addUnauthRoute(
certificateReset => 'certificateReset',
[ 'POST', 'GET' ]
);
# Initialize Captcha if needed
if ( $self->conf->{captcha_mail_enabled} ) {
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
}
# Load registered module
$self->registerModule(
$self->p->loadPlugin(
'::CertificateResetByMail::' . $self->conf->{registerDB}
)
) or return 0;
return 1;
}
# RUNNIG METHODS
# Handle reset requests
sub certificateReset {
my ( $self, $req ) = @_;
$self->p->controlUrl($req);
# Check parameters
$req->error( $self->_certificateReset($req) );
# Display form
my ( $tpl, $prms ) = $self->display($req);
return $self->p->sendHtml( $req, $tpl, params => $prms );
}
sub _certificateReset {
my ( $self, $req ) = @_;
my ( $mailToken, %tplPrms );
# CertificatReset FORM => modifyCertificate()
if (
$req->method =~ /^POST$/i
and ( $req->uploads->{certif} )
)
{
my $upload = $req->uploads->{certif};
return $self->modifyCertificate($req);
}
# FIRST FORM
$mailToken = $req->data->{mailToken} = $req->param('mail_token');
unless ( $req->param('mail') || $mailToken ) {
$self->setSecurity($req);
return PE_MAILFIRSTACCESS if ( $req->method eq 'GET' );
return PE_MAILFORMEMPTY;
}
my $searchByMail = 1;
# OTHER FORMS
if ($mailToken) {
$self->logger->debug("Token given for certificate reset: $mailToken");
# Check if token is valid
my $mailSession =
$self->p->getApacheSession( $mailToken, kind => "TOKEN" );
unless ($mailSession) {
$self->userLogger->warn('Bad reset token');
return PE_BADMAILTOKEN;
}
$req->{user} = $mailSession->data->{user};
$req->data->{mailAddress} =
$mailSession->data->{ $self->conf->{mailSessionKey} };
$self->logger->debug( 'User associated to: ' . $req->{user} );
# Restore pdata if any
$req->pdata( $mailSession->data->{_pdata} || {} );
$mailSession->remove;
$searchByMail = 0 unless ( $req->{user} =~ /\@/ );
}
# Check for values posted
else {
# Use submitted value
$req->{user} = $req->param('mail');
# Check if token exists
my $token;
if ( $self->ottRule->( $req, {} ) or $self->captcha ) {
$token = $req->param('token');
unless ($token) {
$self->setSecurity($req);
$self->userLogger->warn('Reset try without token');
return PE_NOTOKEN;
}
}
# Captcha for register form
if ( $self->captcha ) {
my $captcha = $req->param('captcha');
unless ($captcha) {
$self->userLogger->notice('Reset try with captcha not filled');
# Set captcha or token
$self->setSecurity($req);
return PE_CAPTCHAEMPTY;
}
# Check captcha
unless ( $self->captcha->validateCaptcha( $token, $captcha ) ) {
$self->userLogger->info('Captcha failed: wrong code');
# Set captcha or token
$self->setSecurity($req);
return PE_CAPTCHAERROR;
}
$self->logger->debug('Captcha code verified');
}
elsif ( $self->ottRule->( $req, {} ) ) {
unless ( $self->ott->getToken($token) ) {
$self->setSecurity($req);
$self->userLogger->warn('Reset try with expired/bad token');
return PE_TOKENEXPIRED;
}
}
unless ( $req->{user} =~ /$self->{conf}->{userControl}/o ) {
$self->setSecurity($req);
return PE_MALFORMEDUSER;
}
}
# Search user in database
$req->steps( [
'getUser', 'setSessionInfo',
'setMacros', 'setGroups',
'setPersistentSessionInfo', 'setLocalGroups'
]
);
if ( my $error = $self->p->process( $req, useMail => $searchByMail ) ) {
if ( $error == PE_USERNOTFOUND or $error == PE_BADCREDENTIALS ) {
$self->userLogger->warn( 'Reset asked for an unvalid user ('
. $req->param('mail')
. ')' );
# To avoid mail enumeration, return OK
# unless portalErrorOnMailNotFound is set
if ( $self->conf->{portalErrorOnMailNotFound} ) {
$self->setSecurity($req);
return PE_MAILNOTFOUND;
}
my $mailTimeout =
$self->conf->{mailTimeout} || $self->conf->{timeout};
my $expTimestamp = time() + $mailTimeout;
$req->data->{expMailDate} =
strftime( '%d/%m/%Y', localtime $expTimestamp );
$req->data->{expMailTime} =
strftime( '%H:%M', localtime $expTimestamp );
return PE_MAILCONFIRMOK;
}
return $error;
}
# Build temporary session
my $mailSession = $self->getCertificateSession( $req->{user} );
unless ( $mailSession or $mailToken ) {
# Create a new session
my $infos = {};
# Set _utime for session autoremove
# Use default session timeout and mail session timeout to compute it
my $time = time();
my $timeout = $self->conf->{timeout};
my $mailTimeout = $self->conf->{mailTimeout} || $timeout;
$infos->{_utime} = $time + ( $mailTimeout - $timeout );
# Store expiration timestamp for further use
$infos->{mailSessionTimeoutTimestamp} = $time + $mailTimeout;
# Store start timestamp for further use
$infos->{mailSessionStartTimestamp} = $time;
# Store mail
$infos->{ $self->conf->{mailSessionKey} } =
$self->p->getFirstValue(
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
# Store user
$infos->{user} = $req->{user};
# Store type
$infos->{_type} = 'certificate';
# Store pdata
$infos->{_pdata} = $req->pdata;
# create session
$mailSession =
$self->p->getApacheSession( undef, kind => "TOKEN", info => $infos );
$req->id( $mailSession->id );
}
elsif ($mailSession) {
$self->logger->debug( 'Mail session found: ' . $mailSession->id );
$req->id( $mailSession->id );
$req->data->{mailAlreadySent} = 1;
}
# Send confirmation mail
unless ($mailToken) {
# Mail session expiration date
my $expTimestamp = $mailSession->data->{mailSessionTimeoutTimestamp};
$self->logger->debug("Mail expiration timestamp: $expTimestamp");
$req->data->{expMailDate} =
strftime( '%d/%m/%Y', localtime $expTimestamp );
$req->data->{expMailTime} =
strftime( '%H:%M', localtime $expTimestamp );
# Mail session start date
my $startTimestamp = $mailSession->data->{mailSessionStartTimestamp};
$self->logger->debug("Mail start timestamp: $startTimestamp");
$req->data->{startMailDate} =
strftime( '%d/%m/%Y', localtime $startTimestamp );
$req->data->{startMailTime} =
strftime( '%H:%M', localtime $startTimestamp );
# Ask if user wants an another confirmation email
if ( $req->data->{mailAlreadySent}
and not $req->param('resendconfirmation') )
{
$self->userLogger->notice(
'Reset mail already sent to ' . $req->{user} );
# Return mail already sent only if it is allowed at previous step
if ( $self->conf->{portalErrorOnMailNotFound} ) {
$self->setSecurity($req);
return PE_MAILCONFIRMATION_ALREADY_SENT;
}
}
# Get mail address
$req->data->{mailAddress} ||=
$self->p->getFirstValue(
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
return PE_MAILERROR unless ( $req->data->{mailAddress} );
# Build confirmation url
my $req_url = $req->data->{_url};
my $skin = $self->p->getSkin($req);
my $url =
$self->certificateResetUrl . '?'
. build_urlencoded(
mail_token => $req->{id},
skin => $skin,
( $req_url ? ( url => $req_url ) : () ),
);
# Build mail content
$tplPrms{MAIN_LOGO} = $self->conf->{portalMainLogo};
my $tr = $self->translate($req);
my $subject = $self->conf->{certificateResetByMailStep1Subject};
unless ($subject) {
$subject = 'certificateResetByMailStep1Subject';
$tr->( \$subject );
}
my $body;
my $html;
if ( $self->conf->{certificateResetByMailStep1Body} ) {
# We use a specific text message, no html
$body = $self->conf->{certificateResetByMailStep1Body};
}
else {
# Use HTML template
$body = $self->loadTemplate(
'mail_confirm',
filter => $tr,
params => \%tplPrms
);
$html = 1;
}
# Replace variables in body
$body =~ s/\$expMailDate/$req->data->{expMailDate}/ge;
$body =~ s/\$expMailTime/$req->data->{expMailTime}/ge;
$body =~ s/\$url/$url/g;
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
# Send mail
unless (
$self->sendmail(
$req->data->{mailAddress}, $subject, $body, $html
)
)
{
$self->logger->debug('Unable to send reset mail');
# Don't return an error here to avoid enumeration
}
return PE_MAILCONFIRMOK;
}
# User has a valid mailToken, allow to reset certificate
# A token is required
$self->ott->setToken(
$req,
{
%{ $req->sessionInfo }, certificateResetAllowed => 1
}
);
return PE_RESETCERTIFICATE_FIRSTACCESS if ( $req->method eq 'GET' );
return PE_RESETCERTIFICATE_FOREMPTY;
}
sub modifyCertificate {
my ( $self, $req ) = @_;
my %tplPrms;
my $nbio;
my $x509;
my $notAfter;
$self->logger->debug('Change your Certificate form response');
if ( my $token = $req->param('token') ) {
$req->sessionInfo( $self->ott->getToken($token) );
unless ( $req->sessionInfo ) {
$self->userLogger->warn(
'User tries to change certificate with an invalid or expired token'
);
return PE_NOTOKEN;
}
}
# These 2 cases means that a user tries to reset certificate without
# following valid links!!!
else {
$self->userLogger->error(
'User tries to reset certificate without token');
return PE_NOTOKEN;
}
unless ( $req->sessionInfo->{certificateResetAllowed} ) {
$self->userLogger->error(
'User tries to use another token to reset certificate');
return PE_NOTOKEN;
}
#Updload certificate
my $upload = $req->uploads->{certif};
unless ( $upload->size > 0 ) { return PE_RESETCERTIFICATE_FOREMPTY; }
# Get Certificate
my $file = $upload->path;
$self->userLogger->debug( "Temporaly file " . $file );
# Convert certificate file uploaded on DER format with openssl library
#my $certifbase64 =`openssl x509 -outform der -in $file -out $file`;
# load certificate from file with openssl library
$nbio = Net::SSLeay::BIO_new_file( $file, 'r' ) or die $!;
# for PEM certificate
$x509 = Net::SSLeay::PEM_read_bio_X509($nbio);
Net::SSLeay::BIO_free($nbio);
unless ($x509) {
$self->userLogger->debug( "Unable to decode certificate for user "
. Net::SSLeay::ERR_error_string( Net::SSLeay::ERR_get_error() ) );
#return PE_CERTIFICATE_INVALIDE;
return PE_RESETCERTIFICATE_INVALIDE;
}
$self->userLogger->debug("Certificate decoded successfully");
$notAfter = Net::SSLeay::P_ASN1_TIME_get_isotime(
Net::SSLeay::X509_get_notAfter($x509) );
my $x509issuer = Net::SSLeay::X509_NAME_oneline(
Net::SSLeay::X509_get_issuer_name($x509) );
my $x509serial = Net::SSLeay::P_ASN1_INTEGER_get_hex(
Net::SSLeay::X509_get_serialNumber($x509) );
$self->userLogger->debug(
"Certificate will expire after $notAfter, Issuer $x509issuer and serialNumber $x509serial"
);
# Check Certificate Validity befor store
if (
$self->checkCertificateValidity( $notAfter,
$self->conf->{certificateResetByMailValidityDelay} ) == 0
)
{
$self->userLogger->debug(
"Your cettificate is no longer valid in $self->conf->{certificateValidityDelay}"
);
return PE_RESETCERTIFICATE_INVALIDE;
#return PE_PASSWORD_MISMATCH;
}
# Build serial number hex exemple f3:08:52:63:28:29:fa:e2
my @numberstring = split //, lc($x509serial);
my $serial = "";
for ( my $i = 0 ; $i <= $#numberstring ; $i += 2 ) {
$serial = $serial . $numberstring[$i] . $numberstring[ $i + 1 ];
if ( $i + 2 < $#numberstring ) { $serial = $serial . ":"; }
}
# format issuer in the good format example "CN=CA,OU=CISIRH,O=MINEFI,L=Paris,ST=France,C=FR"
my @issuertab = split /\//, $x509issuer;
shift(@issuertab);
my $issuer = join( ",", reverse(@issuertab) );
#$issuer = lc($issuer);
my $certificatExactAssertion =
'{ serialNumber ' . $serial . ', issuer rdnSequence:"' . $issuer . '" }';
$self->userLogger->debug( "Description:: " . $certificatExactAssertion );
# Get attribut userCertificate;binary value
my $cert = $self->certificateHash($file);
# modif the ldap certificate attribute
$req->user( $req->{sessionInfo}->{_user} );
my $result =
$self->registerModule->modifCertificate( $certificatExactAssertion,
$cert, $req );
$self->{user} = undef;
# Mail token can be used only one time, delete the session if all is ok
#
return $result unless ( $result == PE_OK );
# Send mail to notify the certificate reset sucessfully
$req->data->{mailAddress} ||=
$self->p->getFirstValue(
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
# Build mail content
$tplPrms{MAIN_LOGO} = $self->conf->{portalMainLogo};
my $tr = $self->translate($req);
my $subject = $self->conf->{certificateResetByMailStep2Subject};
unless ($subject) {
$subject = 'certificateResetByMailStep2Subject';
$tr->( \$subject );
}
my $body;
my $html;
if ( $self->conf->{certificateResetByMailStep2Body} ) {
# We use a specific text message, no html
$body = $self->conf->{certificateResetByMailStep2Body};
}
else {
# Use HTML template
$body = $self->loadTemplate(
'mail_certificatReset',
filter => $tr,
params => \%tplPrms
);
$html = 1;
}
# Replace variables in body
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
# Send mail
return PE_MAILERROR
unless $self->sendmail( $req->data->{mailAddress}, $subject, $body,
$html );
return PE_MAILOK;
}
sub setSecurity {
my ( $self, $req ) = @_;
if ( $self->captcha ) {
$self->captcha->setCaptcha($req);
}
elsif ( $self->ottRule->( $req, {} ) ) {
$self->ott->setToken($req);
}
return 1;
}
sub display {
my ( $self, $req ) = @_;
$self->logger->debug( 'Display called with code: ' . $req->error );
my %tplPrm = (
SKIN_PATH => $self->conf->{staticPrefix},
SKIN => $self->p->getSkin($req),
SKIN_BG => $self->conf->{portalSkinBackground},
MAIN_LOGO => $self->conf->{portalMainLogo},
AUTH_ERROR => $req->error,
AUTH_ERROR_TYPE => $req->error_type,
AUTH_URL => $req->data->{_url},
CHOICE_VALUE => $req->{_authChoice},
EXPMAILDATE => $req->data->{expMailDate},
EXPMAILTIME => $req->data->{expMailTime},
STARTMAILDATE => $req->data->{startMailDate},
STARTMAILTIME => $req->data->{startMailTime},
MAILALREADYSENT => $req->data->{mailAlreadySent},
MAIL => (
$self->p->checkXSSAttack( 'mail', $req->{user} )
? ''
: $req->{user}
),
DISPLAY_FORM => 0,
DISPLAY_RESEND_FORM => 0,
DISPLAY_CONFIRMMAILSENT => 0,
DISPLAY_MAILSENT => 0,
DISPLAY_CERTIF_FORM => 0,
);
if ( $req->data->{mailToken}
and
not $self->p->checkXSSAttack( 'mail_token', $req->data->{mailToken} ) )
{
$tplPrm{MAIL_TOKEN} = $req->data->{mailToken};
}
# Display captcha if it's enabled
if ( $req->captcha ) {
$tplPrm{CAPTCHA_SRC} = $req->captcha;
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
}
if ( $req->token ) {
$tplPrm{TOKEN} = $req->token;
}
# Display form the first time
if ( (
$req->error == PE_MAILFORMEMPTY
or $req->error == PE_MAILFIRSTACCESS
or $req->error == PE_MAILNOTFOUND
or $req->error == PE_CAPTCHAERROR
or $req->error == PE_CAPTCHAEMPTY
)
and not $req->data->{mailToken}
)
{
$self->logger->debug('Display form');
$tplPrm{DISPLAY_FORM} = 1;
}
# Display mail confirmation resent form
elsif ( $req->error == PE_MAILCONFIRMATION_ALREADY_SENT ) {
$self->logger->debug('Display resend form');
$tplPrm{DISPLAY_RESEND_FORM} = 1;
}
# Display confirmation mail sent
elsif ( $req->error == PE_MAILCONFIRMOK ) {
$self->logger->debug('Display "confirm mail sent"');
$tplPrm{DISPLAY_CONFIRMMAILSENT} = 1;
}
# Display mail sent
elsif ( $req->error == PE_MAILOK ) {
$self->logger->debug('Display "mail sent"');
$tplPrm{DISPLAY_MAILSENT} = 1;
}
# Display Certificate Reset form
elsif ( $req->data->{mailToken}
and $req->error != PE_MAILERROR
and $req->error != PE_BADMAILTOKEN
and $req->error != PE_MAILOK )
{
$self->logger->debug('Display certificate reset form');
$tplPrm{DISPLAY_CERTIF_FORM} = 1;
}
# Display Certificate Reset form again if certificate invalid
elsif ($req->error == PE_RESETCERTIFICATE_FOREMPTY
|| $req->error == PE_RESETCERTIFICATE_INVALIDE )
{
$self->logger->debug('Display Certificate Reset form');
$tplPrm{DISPLAY_CERTIF_FORM} = 1;
}
return 'certificateReset', \%tplPrm;
}
#tring getCertifResetSession (string mail)
# Check if a certificate reset session exists
# @param mail the value of the mail key in session
# @return the first session id found or nothing if no session
sub getCertificateSession {
my ( $self, $user ) = @_;
my $moduleOptions = $self->conf->{globalStorageOptions} || {};
$moduleOptions->{backend} = $self->conf->{globalStorage};
my $module = "Lemonldap::NG::Common::Apache::Session";
# Search on modifyaccount sessions
my $sessions = $module->searchOn( $moduleOptions, "user", $user );
# Browse found sessions to check if it's a modifyaccount session
foreach my $id ( keys %$sessions ) {
my $certificateResetSession =
$self->p->getApacheSession( $id, ( kind => "TOKEN" ) );
next unless ($certificateResetSession);
return $certificateResetSession
if ( $certificateResetSession->data->{_type}
and $certificateResetSession->data->{_type} =~ /^certificate$/ );
}
# No modifyaccount session found, return empty string
return "";
}
# Use Certificate Update parameter to send mail
sub sendmail {
my ( $self, $mail, $subject, $body, $html ) = @_;
$self->{mailFrom} = $self->conf->{certificateResetByMailSender};
$self->{mailReplyTo} = $self->conf->{certificateResetByMailReplyTo};
return $self->send_mail( $mail, $subject, $body, $html );
}
sub checkCertificateValidity {
my ( $self, $notAfter, $delay ) = @_;
my $dtNow; # now in format DateTime
my $days; # difference between NotAfter and now
my $f = DateTime::Format::RFC3339->new();
my $dtNotAfter = $f->parse_datetime($notAfter);
$self->userLogger->debug("Not After Date: $dtNotAfter");
$dtNow = DateTime->now;
$days = $dtNotAfter->delta_days($dtNow)->delta_days;
$dtNow->add_duration( DateTime::Duration->new( days => $delay ) );
# test if ( now + $validity ) > certificate_expiration
if ( DateTime::compare( $dtNow, $dtNotAfter ) >= 0 ) {
# certificate is about to expire
$self->userLogger->debug(
"Certificate is about to expire or already expired");
return 0;
}
else {
# certificate is still valid
$self->userLogger->debug("Certificate is still valid for $days days");
return 1;
}
}
sub certificateHash {
my ( $self, $file ) = @_;
my $cert;
{
local $/ = undef; # Slurp mode
open CERT, "$file" or die;
$cert = <CERT>;
close CERT;
}
# Normalize certificate
$cert =~ s/-----(BEGIN|END) CERTIFICATE-----//gi;
$cert =~ s/["]//gi;
$cert = decode_base64($cert);
#$self->userLogger->debug( "UserBinary::".$cert);
return $cert;
}
1;

View File

@ -1,4 +1,5 @@
{
"PE-7":"لقد تم قطع اتصالك",
"PE-6":"تم تغير كلمة المرور بنجاح",
"PE0":"تمت مصادقة المستخدم",
@ -84,6 +85,10 @@
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"قبول",
"accessDenied":"ليس لديك إذن بالدخول لهذا التطبيق",
@ -274,5 +279,8 @@
"yourPhone":"رقم هاتفك",
"yourProfile":"ملفك الشخصي",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,10 @@
"PE92":"Zugang zum GET-Service nicht genehmigt",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"Dieser Dienst benötigt Zwei-Faktor-Authentifizierung. Bitte legen Sie ein Gerät an und gehen dann zum Portal zurück.",
"accept":"Akzeptieren",
"accessDenied":"Sie haben keine Zugriffsberechtigung für diese Anwendung",
@ -274,5 +278,8 @@
"yourPhone":"Ihre Telefonnummer",
"yourProfile":"Ihr Profil",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -111,6 +114,7 @@
"checkUser":"Check user SSO profile",
"checkUserMerged":"Check user SSO profile. Some Real and Spoofed SSO groups are merged!",
"checkUserComputeSession":"Computed session data!",
"certificateReset":"Reset my certificate",
"choose2f":"Choose your second factor",
"chooseApp":"Choose an application your are allowed to access to",
"clickHere":"Please click here",
@ -158,6 +162,7 @@
"lastLogins":"Last logins",
"lastName":"Last name",
"linkValidUntil":"This message contains a link to reset your password, this link is valid until ",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"loginHistory":"Login history",
"login":"Login",
"logout":"Logout",
@ -218,6 +223,7 @@
"resendConfirmMail":"Resend confirmation mail?",
"resentConfirm":"Do you want the confirmation mail to be resent?",
"resetFavApps":"Reset my favorite Apps.",
"resetCertificateOK":"Your certificate was reset sucessfully",
"resetPwd":"Reset my password",
"rest2f":"Verification code",
"rightsReloadNeedsLogout": "Rights reloads need to logout and login again",

View File

@ -84,6 +84,9 @@
"PE92":"Acceso no autorizado al servicio GET",
"PE93":"Acceso no concedido al servicio de SUPLANTACIÓN",
"PE94":"Un atributo obligatorio no está presente",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"Este servicio necesita la autenticación de dos factores. Registre un dispositivo ahora, luego reingrese al portal.",
"accept":"Aceptar",
"accessDenied":"No está autorizado a acceder a esta aplicación",
@ -274,5 +277,8 @@
"yourPhone":"Su número telefónico",
"yourProfile":"Su perfil",
"yourTotpKey":"Su llave TOTP",
"yubikey2f":"Yubikey"
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Hyväksy",
"accessDenied":"Sinulla ei ole käyttöoikeutta tähän sovellukseen",
@ -274,5 +277,8 @@
"yourPhone":"Puhelinnumerosi",
"yourProfile":"Profiilisi",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Accès non autorisé au service GET",
"PE93":"Accès non autorisé au service d'Usurpation d'Identité",
"PE94":"Un attribut exigé n'est pas disponible",
"PE95":"votre certificat est invalid ou expire bientot.Veuillez contacter votre administrateur",
"PE96":"Veuillez selectionner votre nouveau certificat",
"PE97":"Veuillez selectionner votre nouveau certificat",
"2fRegRequired":"Ce service requiert une authentification à deux facteurs. Enregistrez un équipement ici et retournez au portail.",
"accept":"Accepter",
"accessDenied":"Vous n'avez pas les droits d'accès à cette application",
@ -274,5 +277,9 @@
"yourPhone":"Votre numéro de téléphone",
"yourProfile":"Vos informations personnelles",
"yourTotpKey":"Votre clef TOTP",
"yubikey2f":"Yubikey"
"yubikey2f":"Yubikey",
"resetCertificateOK":"Votre certificat à été bien réinitialisé",
"linkValidUntilCertif":"Ce message contient un lien pour réinitialiser votre certificat, ce lien est valide jusqu'au ",
"certificateReset":"Réinitialiser votre certificat"
}

View File

@ -84,6 +84,9 @@
"PE92":"Accesso non concesso sul servizio GET",
"PE93":"Accesso non concesso sul servizio IMPERSONATION",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"Questo servizio richiede un'autenticazione a doppio fattore. Registrare un dispositivo ora, quindi tornare al portale.",
"accept":"Accetta",
"accessDenied":"Non hai un'autorizzazione di accesso per questa applicazione",
@ -274,5 +277,8 @@
"yourPhone":"Numero di telefono",
"yourProfile":"Il tuo profilo",
"yourTotpKey":"La tua chiave TOTP",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Onbevoegde toegang tot de GET-service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -274,5 +277,8 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Acesso não autorizado ao serviço GET",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"Um atributo exigido não está disponível",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -274,5 +277,8 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept",
"accessDenied":"You have no access authorization for this application",
@ -274,5 +277,8 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Truy cập không được cấp trên dịch vụ GET",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"Một thuộc tính bắt buộc không có sẵn",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Chấp nhận",
"accessDenied":"Bạn không có quyền truy cập vào ứng dụng này",
@ -274,5 +277,8 @@
"yourPhone":"Số điện thoại của bạn",
"yourProfile":"Profile của bạn",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -84,6 +84,9 @@
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE95":"Your certificate is invalid or expires soon",
"PE96":"Please select your new certificate",
"PE97":"Please select your new certificate",
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
"accept":"Accept 方法",
"accessDenied":"您无权访问此应用",
@ -274,5 +277,8 @@
"yourPhone":"您的电话号码",
"yourProfile":"您的档案",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
"yubikey2f":"Yubikey",
"resetCertificateOK":"Your certificate was reset sucessfully",
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"certificateReset":"Reset my certificate"
}

View File

@ -0,0 +1,182 @@
<TMPL_INCLUDE NAME="header.tpl">
<div id="mailcontent" class="container">
<TMPL_IF NAME="AUTH_ERROR">
<div class="message message-<TMPL_VAR NAME="AUTH_ERROR_TYPE"> alert">
<span trmsg="<TMPL_VAR NAME="AUTH_ERROR">"></span>
</div>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_FORM">
<div class="card">
<form action="#" method="post" class="login" role="form">
<div class="form">
<input type="hidden" name="skin" value="<TMPL_VAR NAME="SKIN">" />
<input type="hidden" name="url" value="<TMPL_VAR NAME="AUTH_URL">" />
<TMPL_IF NAME="CHOICE_VALUE">
<input type="hidden" id="authKey" name="<TMPL_VAR NAME="CHOICE_PARAM">" value="<TMPL_VAR NAME="CHOICE_VALUE">" />
</TMPL_IF>
<h3 trspan="certificateReset"> Reset Your Certificate</h3>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-envelope"></i> </span>
</div>
<input name="mail" type="text" value="<TMPL_VAR NAME="MAIL">" class="form-control" trplaceholder="mail" required />
</div>
<TMPL_IF NAME=CAPTCHA_SRC>
<div class="form-group">
<img src="<TMPL_VAR NAME=CAPTCHA_SRC>" class="img-thumbnail" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required autocomplete="off" />
</div>
</TMPL_IF>
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<button type="submit" class="btn btn-success">
<span class="fa fa-envelope-open"></span>
<span trspan="sendPwd">Send me a link</span>
</button>
</div>
</form>
</div>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_RESEND_FORM">
<div class="card">
<form action="#" method="post" class="login" role="form">
<div class="form">
<input type="hidden" name="skin" value="<TMPL_VAR NAME="SKIN">" />
<input type="hidden" name="url" value="<TMPL_VAR NAME="AUTH_URL">" />
<TMPL_IF NAME="CHOICE_VALUE">
<input type="hidden" id="authKey" name="<TMPL_VAR NAME="CHOICE_PARAM">" value="<TMPL_VAR NAME="CHOICE_VALUE">" />
</TMPL_IF>
<TMPL_IF NAME="MAIL">
<input type="hidden" value="<TMPL_VAR NAME="MAIL">" name="mail">
</TMPL_IF>
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<h3 trspan="resendConfirmMail">Resend confirmation mail?</h3>
<p class="alert alert-info">
<span trspan="pwdResetAlreadyIssued">A Certificate reset request was already issued on</span>
<TMPL_VAR NAME="STARTMAILDATE">.
<span trspan="resentConfirm">Do you want the confirmation mail to be resent?</span>
</p>
<TMPL_IF NAME=CAPTCHA_SRC>
<div class="form-group">
<img src="<TMPL_VAR NAME=CAPTCHA_SRC>" class="img-thumbnail" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required autocomplete="off"/>
</div>
</TMPL_IF>
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<input id="resendconfirmation" type="checkbox" name="resendconfirmation" aria-describedby="resendconfirmationlabel" />
</div>
</div>
<p class="form-control">
<label for="resendconfirmation" id="resendconfirmationlabel" trspan="confirmPwd">Yes , resend the mail</label>
</p>
</div>
<button type="submit" class="btn btn-success">
<span class="fa fa-envelope-open"></span>
<span trspan="submit">Submit</span>
</button>
</div>
</form>
</div>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_CERTIF_FORM">
<div class="card" id="password">
<form action="#" method="post" enctype="multipart/form-data" class="password" role="form">
<div class="form">
<input type="hidden" name="skin" value="<TMPL_VAR NAME="SKIN">" />
<input type="hidden" name="url" value="<TMPL_VAR NAME="AUTH_URL">" />
<TMPL_IF NAME="CHOICE_VALUE">
<input type="hidden" id="authKey" name="<TMPL_VAR NAME="CHOICE_PARAM">" value="<TMPL_VAR NAME="CHOICE_VALUE">" />
</TMPL_IF>
<TMPL_IF NAME="TOKEN">
<input type="hidden" id="token" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<h3 trspan="certificateReset">Reset your Certificate</h3>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-upload"></i> </span>
</div>
<input name="certif" type="file" class="form-control" accept=".pem, .crt,application/x-pem-file", trplaceholder="UploadCertificate" required />
</div>
<button type="submit" class="btn btn-success">
<span class="fa fa-envelope-open"></span>
<span trspan="submit">Submit</span>
</button>
</div>
</form>
</div>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_CONFIRMMAILSENT">
<div class="card">
<form action="#" method="post" class="login" role="form">
<div class="form">
<h3 trspan="mailSent2">A message has been sent to your mail address.</h3>
<p class="alert alert-info">
<span trspan="linkValidUntilCertif">This message contains a link to reset your certificate. This link is valid until </span>
<TMPL_VAR NAME="EXPMAILDATE">.
</p>
</div>
</form>
</div>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_MAILSENT">
<div class="card">
<form action="#" method="post" class="login" role="form">
<div class="form">
<h3 trspan="resetCertificateOK">A confirmation has been sent to your mail address.</h3>
</div>
</form>
</div>
</TMPL_IF>
<div class="buttons">
<a href="<TMPL_VAR NAME="PORTAL_URL">?skin=<TMPL_VAR NAME="SKIN"><TMPL_IF NAME="CHOICE_VALUE">&<TMPL_VAR NAME="CHOICE_PARAM">=<TMPL_VAR NAME="CHOICE_VALUE"></TMPL_IF><TMPL_IF NAME="AUTH_URL">&url=<TMPL_VAR NAME="AUTH_URL"></TMPL_IF>" class="btn btn-primary" role="button">
<span class="fa fa-home"></span>
<span trspan="back2Portal">Go back to portal</span>
</a>
</div>
</div>
<TMPL_INCLUDE NAME="footer.tpl">

View File

@ -53,6 +53,13 @@
<span trspan="resetPwd">Reset my password</span>
</a>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_UPDATECERTIF">
<a class="btn btn-primary" href="<TMPL_VAR NAME="MAILCERTIF_URL">?skin=<TMPL_VAR NAME="SKIN"><TMPL_IF NAME="key">&<TMPL_VAR NAME="CHOICE_PARAM">=<TMPL_VAR NAME="key"></TMPL_IF><TMPL_IF NAME="AUTH_URL">&url=<TMPL_VAR NAME="AUTH_URL"></TMPL_IF>">
<span class="fa fa-refresh"></span>
<span trspan="certificateReset">Reset my certificate</span>
</a>
</TMPL_IF>
<TMPL_IF NAME="DISPLAY_REGISTER">
<a class="btn btn-secondary" href="<TMPL_VAR NAME="REGISTER_URL">?skin=<TMPL_VAR NAME="SKIN"><TMPL_IF NAME="key">&<TMPL_VAR NAME="CHOICE_PARAM">=<TMPL_VAR NAME="key"></TMPL_IF><TMPL_IF NAME="AUTH_URL">&url=<TMPL_VAR NAME="AUTH_URL"></TMPL_IF>">

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset",
"accountCreated":"تم إنشاء حسابك و إرسال كلمة المرور المؤقتة إلى بريدك الإلكتروني.",
"autoMail":"تم إرسال هذا البريد تلقائيا ",
"click2Register":"انقر هنا لتأكيد تسجيل حسابك",
@ -15,4 +16,4 @@
"requestIssuedFromIP":"الطلب قد أرسل من عنوان الآي بي",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"تسجيل الدخول الخاص بك هو"
}
}

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset",
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
"autoMail":"This mail was sent automatically",
"click2Register":"Click here to confirm your account registration",

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset,"
"accountCreated":"Tunnus on luotu, väliaikainen salasana on lähetetty sähköpostiisi.",
"autoMail":"Tämä sähköpostiviesti lähetetään automaattisesti",
"click2Register":"Klikkaa tästä vahvistaaksesi käyttäjätunnuksesi rekisteröinnin",
@ -15,4 +16,4 @@
"requestIssuedFromIP":"The request was issued from IP",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"Your login is"
}
}

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Votre certificat a été réinitialisé avec succés",
"accountCreated":"Votre compte a été créé, un mot de passe temporaire a été envoyé à votre adresse mail.",
"autoMail":"Ceci est un message automatique",
"click2Register":"Cliquez ici pour confirmer l'enregistrement de votre compte",

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset",
"accountCreated":"Il tuo account è stato creato, una password temporanea è stata inviata al tuo indirizzo di posta elettronica.",
"autoMail":"Questa mail é stata inviata automaticamente",
"click2Register":"Clicca qui per confermare la registrazione del tuo account",
@ -15,4 +16,4 @@
"requestIssuedFromIP":"La richiesta è stata emessa da IP",
"yourLoginCodeIs":"Il tuo codice di accesso è",
"yourLoginIs":"Il tuo login é"
}
}

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset",
"accountCreated":"Tài khoản của bạn đã được tạo, mật khẩu tạm thời vừa được gửi đến địa chỉ thư điện tử của bạn.",
"autoMail":"Thư này đã được gửi tự động",
"click2Register":"Nhấn ở đây để xác nhận việc đăng ký tài khoản của bạn",
@ -15,4 +16,4 @@
"requestIssuedFromIP":"Yêu cầu được gửi đi từ địa chỉ IP",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"Đăng nhập của bạn là"
}
}

View File

@ -1,4 +1,5 @@
{
"resetCertificat":"Your certificate was reset",
"accountCreated":"您的账户已被创建,初始密码已发送至您的邮箱",
"autoMail":"此邮件是自动发送的",
"click2Register":"请点击此处已确认您的账户注册",
@ -15,4 +16,4 @@
"requestIssuedFromIP":"此请求来自IP地址",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"您登陆的账户是"
}
}

View File

@ -0,0 +1,9 @@
<TMPL_INCLUDE NAME="mail_header.tpl">
<p>
<span trspan="hello">Hello</span> $cn,<br />
<br />
<span trspan="certificatReset">Your certificate was reset sucessfully.</span>
</p>
<TMPL_INCLUDE NAME="mail_footer.tpl">

View File

@ -0,0 +1,196 @@
#!/usr/bin/perl
use Test::More;
use strict;
use IO::String;
use File::Copy;
BEGIN {
eval {
require 't/test-lib.pm';
require 't/smtp.pm';
};
}
my ($res, $user);
my $maintests = 6;
SKIP: {
eval
'require Email::Sender::Simple; use GD::SecurityImage;use Image::Magick;';
if ($@) {
skip 'Missing dependencies '.$@ , $maintests;
}
skip 'LLNGTESTLDAP is not set', $maintests unless ( $ENV{LLNGTESTLDAP} );
require 't/test-ldap.pm';
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'debug',
useSafeJail => 1,
portalDisplayRegister => 1,
authentication => 'SSL',
userDB => 'LDAP',
passwordDB => 'LDAP',
registerDB => 'LDAP',
ldapServer => 'ldap://127.0.0.1:19389/',
ldapBase => 'ou=users,dc=example,dc=com',
managerDn => 'cn=admin,dc=example,dc=com',
managerPassword => 'admin',
captcha_mail_enabled => 0,
portalDisplayCertificateResetByMail => 1,
certificateResetByMailCeaAttribute => 'description',
certificateResetByMailCertificateAttribute => 'userCertificate;binary',
certificateResetByMailStep1Body => 'Clique here <a href="$url"> to confirm your mail. It will expire $expMailDate',
certificateResetByMailStep2Body => 'Certificate Reset sucessfully!',
certificateValidityDelay => 30
}
}
);
# Test form
# ------------------------
ok( $res = $client->_get('/certificateReset', accept => 'text/html' ),
'Reset form', );
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'mail' );
$query = 'mail=dwho%40badwolf.org';
# Post email
ok(
$res = $client->_post(
'/certificateReset', IO::String->new($query),
length => length($query),
accept => 'text/html'
),
'Post mail'
) ;
ok( mail() =~ m#a href="http://auth.example.com/certificateReset\?(.*?)"#,
'Found link in mail' );
$query = $1;
my $querymail = $query;
ok(
$res =
$client->_get( '/certificateReset', query => $query, accept => 'text/html' ),
'Post mail token received by mail'
);
# print STDERR Dumper($res);
( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' );
ok( $res->[2]->[0] =~ /certif/s, ' Ask for a new certificate file' );
#print STDERR Dumper($query);
my %inputs = split( /[=&]/,$query );
my %querytab = split( /[=&]/,$querymail );
# Create the certificate file
my $cert = "-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIJAKGx8siw7lkRMA0GCSqGSIb3DQEBCwUAMFExCzAJBgNV
BAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxDjAMBgNVBAcMBVBBcmlzMREwDwYDVQQK
DAhMaW5hZ29yYTEOMAwGA1UECwwFTElOSUQwIBcNMTkwNzA0MTcyNjI4WhgPMjEx
OTA2MTAxNzI2MjhaMFExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxDjAM
BgNVBAcMBVBBcmlzMREwDwYDVQQKDAhMaW5hZ29yYTEOMAwGA1UECwwFTElOSUQw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3iyeNE2vpURgdY7xwxS16
xUJANPuMSrCfy1E/xpCtbP02zK0B11DkT81AnTHgvsWYuiubR1P3Phhh+JLsLRho
Grzu9xjaiKXQ+kT1cAiq6skZljphykXBfKUb73W9CPntHL/zl3XyIfu+dWyCGbqa
jHw0Llomi8JqU/XKB6XAYumsV3QzFMM7ECm5HeV3BxfIBwoIOwfwINDUrAGS3h4k
WH/iiqwG7uSuADupSfdmOrvE7rYZupPas4YATX1m5hmON++9pRRFVEoNeOV1qyGY
G7swH1uoO2hAgwKIw0vinft/pJLqe3qhrJwNCIZFHaDEx/PRERFeeEH9/6HSz5kt
AgMBAAGjUDBOMB0GA1UdDgQWBBTFv6pQT/9IBWEAGhILGCcweVfHmTAfBgNVHSME
GDAWgBTFv6pQT/9IBWEAGhILGCcweVfHmTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQBFYneMW5etMnsA3/PdvOqx/ijBF98aKlB4U4IKZpdDRAcsstdL
BSsHRQbHXtb9VdlDWvUnNg5DmjsA8DkOXKXGPGM9ncu9tQi9EoInbOJTMaEsIr2j
zrLj6PHTvazy+6Au+R/9N5u3WQtq/Z2xoN/+bbQ1dyjXgQmBZFizHP32l5AdgBDT
jF7xMHxJ6Jxz9lkI+d9v0TzpxTStsaC+pbDfoouNc2deZkv84YTIrD0EPSHFDH5d
u5i9b+lrWZeCtpVEPzSYpnBwGfepbZAzfVRKJm7wZPCe7KxqMGXQLVBkD8oN7vA1
lkRrWfQftwmLyNIu3HfSgXlgAZS30ymfbzBU
-----END CERTIFICATE-----";
open my $FH2, '>', '/tmp/v296ZJQ_kG';
print {$FH2} "$cert";
close $FH2;
$res = $client->app->( {
'plack.request.query' => bless( {
'skin' => $querytab{'skin'},
'mail_token' => $querytab{'mail_token'}
}, 'Hash::MultiValue' ),
'PATH_INFO' => '/certificateReset',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'REQUEST_METHOD' => 'POST',
'HTTP_ORIGIN' => 'http://auth.example.com',
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
'REQUEST_SCHEME' => 'http',
'HTTP_CACHE_CONTROL' => 'max-age=0',
'plack.request.merged' => bless( {
'skin' => $querytab{'skin'},
'mail_token' => $querytab{'mail_token'},
'url' => '',
'token' => $inputs{'token'}
}, 'Hash::MultiValue' ),
'REMOTE_PORT' => '36674',
'QUERY_STRING' => $querymail,
'SERVER_SIGNATURE' => '',
'psgix.input.buffered' => 1,
'HTTP_UPGRADE_INSECURE_REQUESTS' => '1',
'CONTENT_TYPE' => 'multipart/form-data; boundary=----WebKitFormBoundarybabRY9u6K9tERoLr',
'plack.request.upload' => bless( {
'certif' => bless( {
'headers' => bless( {
'content-disposition' => 'form-data; name="certif"; filename="user.pem"',
'content-type' => 'application/x-x509-ca-cert',
'::std_case' => {
'content-disposition' => 'Content-Disposition'
}
}, 'HTTP::Headers' ),
'filename' => 'user.pem',
'tempname' => '/tmp/v296ZJQ_kG',
'size' => 1261
}, 'Plack::Request::Upload' )
}, 'Hash::MultiValue' ),
'psgi.streaming' => 1,
'plack.request.body' => bless( {
'skin' => 'bootstrap',
'url' => '',
'token' => $inputs{'token'}
}, 'Hash::MultiValue' ),
'SCRIPT_URL' => '/certificateReset',
'SERVER_NAME' => 'auth.example.com',
'HTTP_REFERER' => 'http://auth.example.com/certificateReset?'.$querymail,
'HTTP_CONNECTION' => 'close',
'CONTENT_LENGTH' => '1759',
'SCRIPT_URI' => 'http://auth.example.com/certificateReset',
'plack.cookie.parsed' => {
'llnglanguage' => 'fr'
},
'SERVER_PORT' => '80',
'SERVER_NAME' => 'auth.example.com',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'SCRIPT_NAME' => '',
'HTTP_USER_AGENT' => 'Mozilla/5.0 (VAX-4000; rv:36.0) Gecko/20350101 Firefox',
'HTTP_COOKIE' => 'llnglanguage=fr',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_URI' => '/certificateReset?'.$querymail,
'plack.cookie.string' => 'llnglanguage=fr',
'SERVER_ADDR' => '127.0.0.1',
'psgi.url_scheme' => 'http',
'psgix.harakiri' => '',
'HTTP_HOST' => 'auth.example.com'
});
ok( mail() =~ /Certificate Reset sucessfully/, 'Certificate was changed');
}
count($maintests);
stopLdapServer() if $ENV{LLNGTESTLDAP};
done_testing( count() );