Merge remote-tracking branch 'origin/master' into maxbes/lemonldap-ng-fix-1882-remove-oidcServiceMetaDataIssuer

This commit is contained in:
Clément OUDOT 2019-09-19 15:31:25 +02:00
commit 0466a2c8cc
73 changed files with 1747 additions and 99 deletions

View File

@ -5,6 +5,11 @@
# ~/CN=(?<CN>[^/]+) $CN;
#}
# FastCGI backend definition
upstream llng_portal_upstream {
server unix:__FASTCGISOCKDIR__/llng-fastcgi.sock;
}
server {
listen __PORT__;
server_name auth.__DNSDOMAIN__;
@ -30,7 +35,7 @@ server {
# FastCGI configuration
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:__FASTCGISOCKDIR__/llng-fastcgi.sock;
fastcgi_pass llng_portal_upstream;
fastcgi_param LLTYPE psgi;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_split_path_info ^(.*\.psgi)(/.*)$;
@ -47,6 +52,30 @@ server {
# Uncomment this if you use Auth SSL:
#uwsgi_param SSL_CLIENT_S_DN_CN $ssl_client_s_dn_cn;
# REST/SOAP functions for sessions management (disabled by default)
location ~ ^/index.psgi/adminSessions {
fastcgi_pass llng_portal_upstream;
deny all;
}
# REST/SOAP functions for sessions access (disabled by default)
location ~ ^/index.psgi/sessions {
fastcgi_pass llng_portal_upstream;
deny all;
}
# REST/SOAP functions for configuration access (disabled by default)
location ~ ^/index.psgi/config {
fastcgi_pass llng_portal_upstream;
deny all;
}
# REST/SOAP functions for notification insertion (disabled by default)
location ~ ^/index.psgi/notification {
fastcgi_pass llng_portal_upstream;
deny all;
}
}
index index.psgi;
@ -61,26 +90,6 @@ server {
alias __PORTALSTATICDIR__;
}
# REST/SOAP functions for sessions management (disabled by default)
location /index.psgi/adminSessions {
deny all;
}
# REST/SOAP functions for sessions access (disabled by default)
location /index.psgi/sessions {
deny all;
}
# REST/SOAP functions for configuration access (disabled by default)
location /index.psgi/config {
deny all;
}
# REST/SOAP functions for notification insertion (disabled by default)
location /index.psgi/notification {
deny all;
}
# DEBIAN
# If install was made with USEDEBIANLIBS (official releases), uncomment this
#location /javascript/ {

View File

@ -1153,7 +1153,7 @@ lemonldap-ng (1.2.3) stable; urgency=low
urn:/Lemonldap::NG::Common::CGI::SOAPService
* [LEMONLDAP-546] - Form replay: POST request is not sent
* [LEMONLDAP-541] - Handler SOAP errors : setAttributes is not an
authorizated function
authorized function
* [LEMONLDAP-547] - Update Browseable documentation in case of SAML in use
* [LEMONLDAP-565] - Update META.yml files
* [LEMONLDAP-581] - Clean Perl dependencies

View File

@ -98,7 +98,7 @@ Then, go in <code>OpenID parameters</code>:
</li>
<li class="level1"><div class="li"> <strong>Secret token</strong>: used to check integrity of OpenID response.</div>
</li>
<li class="level1"><div class="li"> <strong>Authorizated domain</strong>:</div>
<li class="level1"><div class="li"> <strong>Authorized domain</strong>:</div>
<ul>
<li class="level2"><div class="li"> <strong>List type</strong>: choose white list to define allowed domains or black list to define forbidden domains</div>
</li>

View File

@ -97,7 +97,7 @@ Sessions for connected users <em>(used by <a href="authproxy.html" class="wikili
Authorizations for connected users (always enabled):
</p>
<ul>
<li class="level1"><div class="li"> GET /mysession/?authorizationfor=&lt;base64-encoded-url&gt;: ask if url is authorizated</div>
<li class="level1"><div class="li"> GET /mysession/?authorizationfor=&lt;base64-encoded-url&gt;: ask if url is authorized</div>
</li>
</ul>

View File

@ -98,7 +98,7 @@ To configure sessions, go in Manager, <code>General Parameters</code> » <code>S
<h1 class="sectionedit2" id="command-line_tools">Command-line tools</h1>
<div class="level1">
<ul>
<li class="level1"><div class="li"> LLNG Portal provides a simple tool to delete a session: <code>llngDeleteSession</code>. To use it, simply give it the user identifier <em>(wildcard are authorizated)</em>:</div>
<li class="level1"><div class="li"> LLNG Portal provides a simple tool to delete a session: <code>llngDeleteSession</code>. To use it, simply give it the user identifier <em>(wildcard are authorized)</em>:</div>
</li>
</ul>
<pre class="code shell"># Delete all sessions opened by user &quot;dwho&quot;

View File

@ -68,7 +68,7 @@ SOAP functions are not accessible by network by default. SOAP functions are prot
</li>
<li class="level2"><div class="li"> <strong>isAuthorizedURI(cookieValue,url)</strong>: check if user is granted to access to the function</div>
</li>
<li class="level2"><div class="li"> <strong>getMenuApplications(cookieValue)</strong>: return a list of authorizated applications (based on menu calculation)</div>
<li class="level2"><div class="li"> <strong>getMenuApplications(cookieValue)</strong>: return a list of authorized applications (based on menu calculation)</div>
</li>
</ul>
</li>

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

@ -11,8 +11,8 @@ our $VERSION = '2.1.0';
## @cmethod Lemonldap::NG::Common::PSGI::SOAPService new(object obj,string @func)
# Constructor
# @param $obj object which will be called for SOAP authorizated methods
# @param @func authorizated methods
# @param $obj object which will be called for SOAP authorized methods
# @param @func authorized methods
# @return Lemonldap::NG::Common::PSGI::SOAPService object
sub new {
my ( $class, $obj, $req, @func ) = @_;
@ -24,7 +24,7 @@ sub new {
# Call the wanted function with the object given to the constructor.
# AUTOLOAD() is a magic method called by Perl interpreter fon non existent
# functions. Here, we use it to call the wanted function (given by $AUTOLOAD)
# if it is authorizated
# if it is authorized
# @return data provided by the exported function
sub AUTOLOAD {
my $self = shift;

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

@ -96,6 +96,7 @@ sub set_custom {
sub set_header_in {
my ( $class, $request, %headers ) = @_;
while ( my ( $h, $v ) = each %headers ) {
utf8::downgrade($v);
$request->env->{'psgi.r'}->headers_in->set( $h => $v );
}
}

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_INVALID',
'96' => 'PE_RESETCERTIFICATE_FOREMPTY',
'97' => 'PE_RESETCERTIFICATE_FIRSTACCESS'
};
}

View File

@ -29,7 +29,7 @@ count(4);
# Authentified queries
# --------------------
# Authorizated query
# Authorized query
ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );

View File

@ -19,7 +19,7 @@ ok(
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps'
),
'Authorizated query'
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
@ -30,7 +30,7 @@ ok(
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps'
),
'Authorizated query'
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);

View File

@ -32,7 +32,7 @@ count(4);
# Authentified queries
# --------------------
# Authorizated query
# Authorized query
ok(
$res =
$client->_get( '/', undef, 'test.example.org', "lemonldap=$sessionId" ),

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'
},
@ -2324,6 +2360,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

@ -498,7 +498,7 @@ EOF
printf STDERR $format, $self->handlerStatusConstantsFile;
# Handler Status file
my $content = <<EOF;
$content = <<EOF;
# This file is generated by $module. Don't modify it by hand
package Lemonldap::NG::Handler::Lib::StatusConstants;

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_INVALID => 95,
PE_RESETCERTIFICATE_FOREMPTY => 96,
PE_RESETCERTIFICATE_FIRSTACCESS => 97,
};
}

View File

@ -74,7 +74,8 @@ sub tree {
'portalCheckLogins',
'portalDisplayResetPassword',
'passwordResetAllowedRetries',
'portalDisplayRegister'
'portalDisplayRegister',
'portalDisplayCertificateResetByMail',
]
},
{
@ -91,7 +92,6 @@ sub tree {
'passwordPolicyMinUpper',
'passwordPolicyMinDigit',
'portalDisplayPasswordPolicy',
'portalDisplayGeneratePassword',
]
},
{
@ -660,11 +660,41 @@ sub tree {
title => 'mailOther',
form => 'simpleInputContainer',
nodes => [
'mailUrl', 'mailTimeout',
'mailUrl',
'mailTimeout',
'portalDisplayGeneratePassword',
'randomPasswordRegexp',
]
}
]
},
{
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":"Certificate 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":"Certificate 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":"Certificate 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":"Certificate 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":"Certificate 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":"Certificate 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":"Certificate 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

@ -301,12 +301,18 @@ sub run {
}
# Delete TOTP 2F device
my $TOTPName;
foreach (@$_2fDevices) {
$TOTPName = $_->{name} if $_->{epoch} eq $epoch;
}
@$_2fDevices = grep { $_->{epoch} ne $epoch } @$_2fDevices;
$self->logger->debug(
"Delete 2F Device : { type => 'TOTP', epoch => $epoch }");
"Delete 2F Device : { type => 'TOTP', epoch => $epoch, name => $TOTPName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice('TOTP deletion succeed');
$self->userLogger->notice(
"TOTP $TOTPName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],

View File

@ -288,12 +288,18 @@ sub run {
}
# Delete U2F device
my $keyName;
foreach (@$_2fDevices) {
$keyName = $_->{name} if $_->{epoch} eq $epoch;
}
@$_2fDevices = grep { $_->{epoch} ne $epoch } @$_2fDevices;
$self->logger->debug(
"Delete 2F Device : { type => 'U2F', epoch => $epoch }");
"Delete 2F Device : { type => 'U2F', epoch => $epoch, name => $keyName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice('U2F key unregistration succeed');
$self->userLogger->notice(
"U2F key $keyName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],

View File

@ -181,12 +181,18 @@ sub run {
}
# Delete Yubikey device
my $UBKName;
foreach (@$_2fDevices) {
$UBKName = $_->{name} if $_->{epoch} eq $epoch;
}
@$_2fDevices = grep { $_->{epoch} ne $epoch } @$_2fDevices;
$self->logger->debug(
"Delete 2F Device : { type => 'UBK', epoch => $epoch }");
"Delete 2F Device : { type => 'UBK', epoch => $epoch, name => $UBKName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice('Yubikey deletion succeed');
$self->userLogger->notice(
"Yubikey $UBKName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],

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

@ -255,7 +255,7 @@ sub run {
unless ( $rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->warn( 'User '
. $req->sessionInfo->{ $self->conf->{whatToTrace} }
. "was not authorized to access to $rp" );
. " was not authorized to access to $rp" );
return PE_UNAUTHORIZEDPARTNER;
}
}

View File

@ -388,7 +388,7 @@ sub run {
unless ( $rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->warn( 'User '
. $req->sessionInfo->{ $self->conf->{whatToTrace} }
. "was not authorizated to access to $sp" );
. " was not authorized to access to $sp" );
return PE_UNAUTHORIZEDPARTNER;
}
}

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_INVALID => 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_INVALID',
'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_INVALID',
'PE_RESETCERTIFICATE_FOREMPTY',
'PE_RESETCERTIFICATE_FIRSTACCESS'
);
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK, 'import' ], );

View File

@ -291,7 +291,12 @@ sub display {
# 3 Authentication has been refused OR first access
$skinfile = 'login';
my $login = $self->userId($req);
$login = '' if ( $login eq 'anonymous' );
if ( $login eq 'anonymous' ) {
$login = '';
}
elsif ( $req->user ) {
$login = $req->{user};
}
%templateParams = (
AUTH_ERROR => $req->error,
AUTH_ERROR_TYPE => $req->error_type,
@ -302,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

@ -509,6 +509,9 @@ sub buildCookie {
);
}
}
$self->userLogger->notice(
"User $req->{user} successfully authenticated at level $req->{sessionInfo}->{authenticationLevel}"
);
PE_OK;
}

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_INVALID
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_INVALID;
return PE_RESETCERTIFICATE_INVALID;
}
$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_INVALID;
#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_INVALID )
{
$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

@ -36,7 +36,7 @@
# - Authorizations for connected users (always):
# * GET /mysession/?whoami : get "my" uid
# * GET /mysession/?authorizationfor=<base64-encoded-url>: ask if url is
# authorizated
# authorized
# * PUT /mysession/<type> : update some
# persistent data
# (restricted)

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.8
// Generated by CoffeeScript 1.12.7
/*
LemonLDAP::NG TOTP registration script

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

@ -117,7 +117,7 @@ m#iframe src="http://auth.idp.com(/saml/relaySingleLogoutPOST)\?(relay=.*?)"#s,
ok(
getHeader( $res, 'Content-Security-Policy' ) =~
/child-src auth.idp.com/,
' Frame is authorizated'
' Frame is authorized'
)
or explain( $res->[1],
'Content-Security-Policy => ...child-src auth.idp.com' );
@ -132,7 +132,7 @@ m#iframe src="http://auth.idp.com(/saml/relaySingleLogoutPOST)\?(relay=.*?)"#s,
'Get iframe'
);
ok( getHeader( $res, 'Content-Security-Policy' ) !~ /frame-ancestors/,
' Framing authorizated' )
' Framing authorized' )
or explain( $res->[1], 'No frame-ancestors' );
( $host, $url, $query ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleLogout',

View File

@ -126,7 +126,7 @@ m#iframe src="http://auth.sp.com(/saml/proxySingleLogout)\?(SAMLRequest=.*?)"#,
my $query = $2;
ok(
getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.sp.com/,
'Frame is authorizated'
'Frame is authorized'
)
or explain( $res->[1],
'Content-Security-Policy => ...child-src auth.idp.com' );

View File

@ -209,7 +209,7 @@ count(1);
my $url = $1;
$query = $2;
ok( getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.idp.com/,
'Frame is authorizated' )
'Frame is authorized' )
or
explain( $res->[1], 'Content-Security-Policy => ...child-src auth.idp.com' );
count(1);

View File

@ -209,7 +209,7 @@ count(1);
my $url = $1;
$query = $2;
ok( getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.idp.com/,
'Frame is authorizated' )
'Frame is authorized' )
or
explain( $res->[1], 'Content-Security-Policy => ...child-src auth.idp.com' );
count(1);

View File

@ -177,7 +177,7 @@ count(1);
my $url = $1;
$query = $2;
ok( getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.idp.com/,
'Frame is authorizated' )
'Frame is authorized' )
or
explain( $res->[1], 'Content-Security-Policy => ...child-src auth.idp.com' );
count(1);

View File

@ -169,7 +169,7 @@ count(1);
my $url = $1;
$query = $2;
ok( getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.idp.com/,
'Frame is authorizated' )
'Frame is authorized' )
or
explain( $res->[1], 'Content-Security-Policy => ...child-src auth.idp.com' );
count(1);

View File

@ -262,7 +262,7 @@ SKIP: {
ok(
getHeader( $res, 'Content-Security-Policy' ) =~
/child-src auth.idp.com/,
'Frame is authorizated'
'Frame is authorized'
)
or explain( $res->[1],
'Content-Security-Policy => ...child-src auth.idp.com' );

View File

@ -0,0 +1,218 @@
#!/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 =>
'Click 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() );