Append unit test (#2338)

This commit is contained in:
Christophe Maudoux 2020-10-09 22:29:56 +02:00
parent 01031d8c4f
commit 9245fc4ee1
36 changed files with 1030 additions and 70 deletions

View File

@ -223,13 +223,11 @@ site/htdocs/static/bwr/font-awesome/fonts/fontawesome-webfont.woff
site/htdocs/static/bwr/font-awesome/fonts/fontawesome-webfont.woff2
site/htdocs/static/bwr/jquery-ui/jquery-ui.js
site/htdocs/static/bwr/jquery-ui/jquery-ui.min.js
site/htdocs/static/bwr/jquery-ui/jquery-ui.min.js.map
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.js
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.min.js
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.min.js.map
site/htdocs/static/bwr/jquery/dist/jquery.js
site/htdocs/static/bwr/jquery/dist/jquery.min.js
site/htdocs/static/bwr/jquery/dist/jquery.min.js.map
site/htdocs/static/bwr/jquery/dist/jquery.min.map
site/htdocs/static/bwr/qrious/dist/qrious.js
site/htdocs/static/bwr/qrious/dist/qrious.js.map
@ -658,16 +656,18 @@ t/67-CheckUser-with-rules.t
t/67-CheckUser-with-token.t
t/67-CheckUser-with-UnrestrictedUser.t
t/67-CheckUser.t
t/68-ContextSwitching-with-2F-allowed.t
t/68-ContextSwitching-with-2F.t
t/68-ContextSwitching-with-Impersonation.t
t/68-ContextSwitching-with-Logout.t
t/68-ContextSwitching-with-TOTP-and-Notification.t
t/68-ContextSwitching-with-UnrestrictedUser.t
t/68-ContextSwitching.t
t/68-Impersonation-with-2F.t
t/68-Impersonation-with-doubleCookies.t
t/68-Impersonation-with-filtered-merge.t
t/68-Impersonation-with-History.t
t/68-Impersonation-with-merge.t
t/68-Impersonation-with-SFA.t
t/68-Impersonation-with-TOTP.t
t/68-Impersonation-with-UnrestrictedUser.t
t/68-Impersonation.t

View File

@ -38,7 +38,7 @@ sub run {
# Check if TOTP can be updated
return $self->p->sendError( $req, 'notAuthorized', 400 )
unless $self->allowedUpdateSfa($req, $action);
unless $self->allowedUpdateSfa( $req, $action );
# Verification that user has a valid TOTP app
if ( $action eq 'verify' ) {
@ -292,18 +292,26 @@ sub run {
if ( $_->{epoch} eq $epoch ) { $TOTPName = $_->{name}; () }
else { $_ }
} @$_2fDevices;
$self->logger->debug(
if ($TOTPName) {
$self->logger->debug(
"Delete 2F Device: { type => 'TOTP', epoch => $epoch, name => $TOTPName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"TOTP $TOTPName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
['{"result":1}']
];
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"TOTP $TOTPName unregistration succeeds for $user");
return [
200,
[
'Content-Type' => 'application/json',
'Content-Length' => 12,
],
['{"result":1}']
];
}
else {
$self->p->sendError( $req, '2FDeviceNotFound', 200 );
}
}
else {
$self->logger->error("Unknown TOTP action -> $action");

View File

@ -35,7 +35,7 @@ sub run {
# Check if U2F key can be updated
return $self->p->sendError( $req, 'notAuthorized', 400 )
unless $self->allowedUpdateSfa($req, $action);
unless $self->allowedUpdateSfa( $req, $action );
if ( $action eq 'register' ) {
@ -279,18 +279,26 @@ sub run {
if ( $_->{epoch} eq $epoch ) { $keyName = $_->{name}; () }
else { $_ }
} @$_2fDevices;
$self->logger->debug(
"Delete 2F Device : { type => 'U2F', epoch => $epoch, name => $keyName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"U2F key $keyName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
['{"result":1}']
];
if ($keyName) {
$self->logger->debug(
"Delete 2F Device: { type => 'U2F', epoch => $epoch, name => $keyName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"U2F key $keyName unregistration succeeds for $user");
return [
200,
[
'Content-Type' => 'application/json',
'Content-Length' => 12,
],
['{"result":1}']
];
}
else {
$self->p->sendError( $req, '2FDeviceNotFound', 200 );
}
}
else {
$self->logger->error("Unknown U2F action -> $action");

View File

@ -42,7 +42,7 @@ sub run {
RAW_ERROR => 'notAuthorized',
AUTH_ERROR_TYPE => 'warning',
}
) unless $self->allowedUpdateSfa($req, $action);
) unless $self->allowedUpdateSfa( $req, $action );
if ( $action eq 'register' ) {
my $otp = $req->param('otp');
@ -190,18 +190,26 @@ sub run {
if ( $_->{epoch} eq $epoch ) { $UBKName = $_->{name}; () }
else { $_ }
} @$_2fDevices;
$self->logger->debug(
if ($UBKName) {
$self->logger->debug(
"Delete 2F Device: { type => 'UBK', epoch => $epoch, name => $UBKName }"
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"Yubikey $UBKName unregistration succeeds for $user");
return [
200,
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
['{"result":1}']
];
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"Yubikey $UBKName unregistration succeeds for $user");
return [
200,
[
'Content-Type' => 'application/json',
'Content-Length' => 12,
],
['{"result":1}']
];
}
else {
$self->p->sendError( $req, '2FDeviceNotFound', 200 );
}
}
else {
$self->logger->error("Unknown Yubikey action -> $action");

View File

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

View File

@ -1 +1 @@
(function(){var e,t,o;o=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning alert-success alert-warning"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},t=function(e,r,t){var n;if(console.log("Error",t),(n=JSON.parse(e.responseText))&&n.error)return n=n.error.replace(/.* /,""),console.log("Returned error",n),n.match(/module/)?o("notAuthorized","warning"):o(n,"warning")},e=function(e,r){return"U2F"===e?e="u":"UBK"===e?e="yubikey":"TOTP"===e?e="totp":o("u2fFailed","warning"),$.ajax({type:"POST",url:portal+"2fregisters/"+e+"/delete",data:{epoch:r},dataType:"json",error:t,success:function(e){return e.error?e.error.match(/notAuthorized/)?o("notAuthorized","warning"):o("unknownAction","warning"):e.result?($("#delete-"+r).hide(),o("yourKeyIsUnregistered","positive")):void 0}})},$(document).ready(function(){return $("body").on("click",".btn-danger",function(){return e($(this).attr("device"),$(this).attr("epoch"))}),$("#goback").attr("href",portal),$(".data-epoch").each(function(){var e;return e=new Date(1e3*$(this).text()),$(this).text(e.toLocaleString())})})}).call(this);
(function(){var e,t,o;o=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning alert-success alert-warning"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},t=function(e,r,t){var n;if(console.log("Error",t),(n=JSON.parse(e.responseText))&&n.error)return n=n.error.replace(/.* /,""),console.log("Returned error",n),n.match(/module/)?o("notAuthorized","warning"):o(n,"warning")},e=function(e,r){return"U2F"===e?e="u":"UBK"===e?e="yubikey":"TOTP"===e?e="totp":o("u2fFailed","warning"),$.ajax({type:"POST",url:portal+"2fregisters/"+e+"/delete",data:{epoch:r},dataType:"json",error:t,success:function(e){return e.error?e.error.match(/notAuthorized/)?o("notAuthorized","warning"):o("unknownAction","warning"):e.result?($("#delete-"+r).hide(),o("yourKeyIsUnregistered","positive")):void 0},error:t})},$(document).ready(function(){return $("body").on("click",".btn-danger",function(){return e($(this).attr("device"),$(this).attr("epoch"))}),$("#goback").attr("href",portal),$(".data-epoch").each(function(){var e;return e=new Date(1e3*$(this).text()),$(this).text(e.toLocaleString())})})}).call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["2fregistration.js"],"names":["delete2F","displayError","setMsg","msg","level","$","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","match","device","epoch","ajax","type","url","portal","data","dataType","success","resp","result","hide","document","ready","on","this","attr","each","myDate","Date","text","toLocaleString","call"],"mappings":"CAMA,WACE,IAAIA,EAAUC,EAAcC,EAE5BA,EAAS,SAASC,EAAKC,GAOrB,OANAC,EAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,gEACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCH,EAAe,SAASU,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GAC1BA,EAAIQ,MAAM,UACLpB,EAAO,gBAAiB,WAExBA,EAAOY,EAAK,YAKzBd,EAAW,SAASuB,EAAQC,GAU1B,MATe,QAAXD,EACFA,EAAS,IACW,QAAXA,EACTA,EAAS,UACW,SAAXA,EACTA,EAAS,OAETrB,EAAO,YAAa,WAEfG,EAAEoB,KAAK,CAAAC,KACN,OADMC,IAEPC,OAAS,eAAiBL,EAAS,UAF5BM,KAGN,CACJL,MAAOA,GAJGM,SAMF,OANEV,MAoBLnB,EApBK8B,QAQH,SAASC,GAChB,OAAIA,EAAKZ,MACHY,EAAKZ,MAAME,MAAM,iBACZpB,EAAO,gBAAiB,WAExBA,EAAO,gBAAiB,WAExB8B,EAAKC,QACd5B,EAAE,WAAamB,GAAOU,OACfhC,EAAO,wBAAyB,kBAFlC,MASbG,EAAE8B,UAAUC,MAAM,WAKhB,OAJA/B,EAAE,QAAQgC,GAAG,QAAS,cAAe,WACnC,OAAOrC,EAASK,EAAEiC,MAAMC,KAAK,UAAWlC,EAAEiC,MAAMC,KAAK,YAEvDlC,EAAE,WAAWkC,KAAK,OAAQX,QACnBvB,EAAE,eAAemC,KAAK,WAC3B,IAAIC,EAEJ,OADAA,EAAS,IAAIC,KAAsB,IAAjBrC,EAAEiC,MAAMK,QACnBtC,EAAEiC,MAAMK,KAAKF,EAAOG,wBAI9BC,KAAKP"}
{"version":3,"sources":["2fregistration.js"],"names":["delete2F","displayError","setMsg","msg","level","$","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","match","device","epoch","ajax","type","url","portal","data","dataType","success","resp","result","hide","document","ready","on","this","attr","each","myDate","Date","text","toLocaleString","call"],"mappings":"CAMA,WACE,IAAIA,EAAUC,EAAcC,EAE5BA,EAAS,SAASC,EAAKC,GAOrB,OANAC,EAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,gEACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCH,EAAe,SAASU,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GAC1BA,EAAIQ,MAAM,UACLpB,EAAO,gBAAiB,WAExBA,EAAOY,EAAK,YAKzBd,EAAW,SAASuB,EAAQC,GAU1B,MATe,QAAXD,EACFA,EAAS,IACW,QAAXA,EACTA,EAAS,UACW,SAAXA,EACTA,EAAS,OAETrB,EAAO,YAAa,WAEfG,EAAEoB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,eAAiBL,EAAS,UACxCM,KAAM,CACJL,MAAOA,GAETM,SAAU,OACVV,MAAOnB,EACP8B,QAAS,SAASC,GAChB,OAAIA,EAAKZ,MACHY,EAAKZ,MAAME,MAAM,iBACZpB,EAAO,gBAAiB,WAExBA,EAAO,gBAAiB,WAExB8B,EAAKC,QACd5B,EAAE,WAAamB,GAAOU,OACfhC,EAAO,wBAAyB,kBAFlC,GAKTkB,MAAOnB,KAIXI,EAAE8B,UAAUC,MAAM,WAKhB,OAJA/B,EAAE,QAAQgC,GAAG,QAAS,cAAe,WACnC,OAAOrC,EAASK,EAAEiC,MAAMC,KAAK,UAAWlC,EAAEiC,MAAMC,KAAK,YAEvDlC,EAAE,WAAWkC,KAAK,OAAQX,QACnBvB,EAAE,eAAemC,KAAK,WAC3B,IAAIC,EAEJ,OADAA,EAAS,IAAIC,KAAsB,IAAjBrC,EAAEiC,MAAMK,QACnBtC,EAAEiC,MAAMK,KAAKF,EAAOG,wBAI9BC,KAAKP"}

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
$(document).ready(function() {
return $('#upgrd').submit();

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var renewCaptcha;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var go, i, timer, timerIsEnabled;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var go, i, timer;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
$(document).ready(function() {
return $(".idploop").on('click', function() {

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var _go, go, i, stop, timer;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
$(document).ready(function() {
return $.ajax((window.location.href.match(/\/upgradesession/) ? window.location.href : portal) + '?kerberos=1', {

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
$(document).ready(function() {
return $.ajax(portal + '?kerberos=1', {

View File

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

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var values;

View File

@ -1 +1 @@
(function(){var o;o={},$(document).ready(function(){return $("script[type='application/init']").each(function(){var e,n,t;try{for(e in t=JSON.parse($(this).text()),n=[],t)n.push(o[e]=t[e]);return n}catch(e){return console.log("Parsing error",e)}}),window.addEventListener("message",function(e){var n,t,o,r,i;return t=e.data,console.log("message=",t),n=decodeURIComponent(t.split(" ")[0]),r=decodeURIComponent(t.split(" ")[1]),o=decodeURIComponent(r.split(".")[1]),i=r===btoa(n+" "+e.origin+" "+o)+"."+o?"unchanged":"changed",e.source.postMessage(i,e.origin)},!1)})}).call(this);
(function(){var r;r={},$(document).ready(function(){return $("script[type='application/init']").each(function(){var n,e,t,o;try{for(e in o=JSON.parse($(this).text()),t=[],o)t.push(r[e]=o[e]);return t}catch(e){return n=e,console.log("Parsing error",n)}}),window.addEventListener("message",function(e){var n,t,o,r,i;return t=e.data,console.log("message=",t),n=decodeURIComponent(t.split(" ")[0]),r=decodeURIComponent(t.split(" ")[1]),o=decodeURIComponent(r.split(".")[1]),i=r===btoa(n+" "+e.origin+" "+o)+"."+o?"unchanged":"changed",e.source.postMessage(i,e.origin)},!1)})}).call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["oidcchecksession.js"],"names":["values","$","document","ready","each","k","results","tmp","JSON","parse","this","text","push","error","console","log","window","addEventListener","e","client_id","message","salt","session_state","stat","data","decodeURIComponent","split","btoa","origin","source","postMessage","call"],"mappings":"CACA,WACE,IAAIA,EAEJA,EAAS,GAETC,EAAEC,UAAUC,MAAM,WAehB,OAdAF,EAAE,mCAAmCG,KAAK,WACxC,IAAOC,EAAGC,EAASC,EACnB,IAGE,IAAKF,KAFLE,EAAMC,KAAKC,MAAMR,EAAES,MAAMC,QACzBL,EAAU,GACAC,EACRD,EAAQM,KAAKZ,EAAOK,GAAKE,EAAIF,IAE/B,OAAOC,EACP,MAAOO,GAEP,OAAOC,QAAQC,IAAI,gBADfF,MAIDG,OAAOC,iBAAiB,UAAW,SAASC,GACjD,IAAIC,EAAWC,EAASC,EAAMC,EAAmBC,EAYjD,OAXAH,EAAUF,EAAEM,KACZV,QAAQC,IAAI,WAAYK,GACxBD,EAAYM,mBAAmBL,EAAQM,MAAM,KAAK,IAClDJ,EAAgBG,mBAAmBL,EAAQM,MAAM,KAAK,IACtDL,EAAOI,mBAAmBH,EAAcI,MAAM,KAAK,IAGjDH,EADED,IADCK,KAAKR,EAAY,IAAMD,EAAEU,OAAS,IAAMP,GAAQ,IAAMA,EAElD,YAEA,UAEFH,EAAEW,OAAOC,YAAYP,EAAML,EAAEU,UACnC,OAGJG,KAAKrB"}
{"version":3,"sources":["oidcchecksession.js"],"names":["values","$","document","ready","each","e","k","results","tmp","JSON","parse","this","text","push","error","console","log","window","addEventListener","client_id","message","salt","session_state","stat","data","decodeURIComponent","split","btoa","origin","source","postMessage","call"],"mappings":"CACA,WACE,IAAIA,EAEJA,EAAS,GAETC,EAAEC,UAAUC,MAAM,WAehB,OAdAF,EAAE,mCAAmCG,KAAK,WACxC,IAAIC,EAAGC,EAAGC,EAASC,EACnB,IAGE,IAAKF,KAFLE,EAAMC,KAAKC,MAAMT,EAAEU,MAAMC,QACzBL,EAAU,GACAC,EACRD,EAAQM,KAAKb,EAAOM,GAAKE,EAAIF,IAE/B,OAAOC,EACP,MAAOO,GAEP,OADAT,EAAIS,EACGC,QAAQC,IAAI,gBAAiBX,MAGjCY,OAAOC,iBAAiB,UAAW,SAASb,GACjD,IAAIc,EAAWC,EAASC,EAAMC,EAAmBC,EAYjD,OAXAH,EAAUf,EAAEmB,KACZT,QAAQC,IAAI,WAAYI,GACxBD,EAAYM,mBAAmBL,EAAQM,MAAM,KAAK,IAClDJ,EAAgBG,mBAAmBL,EAAQM,MAAM,KAAK,IACtDL,EAAOI,mBAAmBH,EAAcI,MAAM,KAAK,IAGjDH,EADED,IADCK,KAAKR,EAAY,IAAMd,EAAEuB,OAAS,IAAMP,GAAQ,IAAMA,EAElD,YAEA,UAEFhB,EAAEwB,OAAOC,YAAYP,EAAMlB,EAAEuB,UACnC,OAGJG,KAAKpB"}

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
document.onreadystatechange = function() {
var redirect;

View File

@ -15,7 +15,6 @@
go = function() {
return Fingerprint2.get(function(components) {
var result, values;
console.error(components);
values = components.map((function(_this) {
return function(component) {
return component.value;

View File

@ -1 +1 @@
(function(){var n;$(document).ready(function(){return window.requestIdleCallback?requestIdleCallback(function(){return n()}):setTimeout(n,500)}),n=function(){return Fingerprint2.get(function(n){var r,t;return console.error(n),t=n.map(function(n){return n.value}),r=Fingerprint2.x64hash128(t.join(""),31),$("#fg").attr("value",r),$("#form").submit()})}}).call(this);
(function(){var n;$(document).ready(function(){return window.requestIdleCallback?requestIdleCallback(function(){return n()}):setTimeout(n,500)}),n=function(){return Fingerprint2.get(function(n){var t,r;return r=n.map(function(n){return n.value}),t=Fingerprint2.x64hash128(r.join(""),31),$("#fg").attr("value",t),$("#form").submit()})}}).call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["registerbrowser.js"],"names":["go","$","document","ready","window","requestIdleCallback","setTimeout","Fingerprint2","get","components","result","values","console","error","map","component","value","x64hash128","join","attr","submit","call","this"],"mappings":"CACA,WACE,IAAIA,EAEJC,EAAEC,UAAUC,MAAM,WAChB,OAAIC,OAAOC,oBACFA,oBAAoB,WACzB,OAAOL,MAGFM,WAAWN,EAAI,OAI1BA,EAAK,WACH,OAAOO,aAAaC,IAAI,SAASC,GAC/B,IAAIC,EAAQC,EASZ,OARAC,QAAQC,MAAMJ,GACdE,EAASF,EAAWK,IACX,SAASC,GACd,OAAOA,EAAUC,QAGrBN,EAASH,aAAaU,WAAWN,EAAOO,KAAK,IAAK,IAClDjB,EAAE,OAAOkB,KAAK,QAAST,GAChBT,EAAE,SAASmB,cAIrBC,KAAKC"}
{"version":3,"sources":["registerbrowser.js"],"names":["go","$","document","ready","window","requestIdleCallback","setTimeout","Fingerprint2","get","components","result","values","map","component","value","x64hash128","join","attr","submit","call","this"],"mappings":"CACA,WACE,IAAIA,EAEJC,EAAEC,UAAUC,MAAM,WAChB,OAAIC,OAAOC,oBACFA,oBAAoB,WACzB,OAAOL,MAGFM,WAAWN,EAAI,OAI1BA,EAAK,WACH,OAAOO,aAAaC,IAAI,SAASC,GAC/B,IAAIC,EAAQC,EAQZ,OAPAA,EAASF,EAAWG,IACX,SAASC,GACd,OAAOA,EAAUC,QAGrBJ,EAASH,aAAaQ,WAAWJ,EAAOK,KAAK,IAAK,IAClDf,EAAE,OAAOgB,KAAK,QAASP,GAChBT,EAAE,SAASiB,cAIrBC,KAAKC"}

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var sendUrl, tryssl;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
(function() {
var sendUrl, tryssl;

View File

@ -1 +1 @@
(function(){var a=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},r=function(e,r,t){var o;if(console.log("Error",t),(o=JSON.parse(e.responseText))&&o.error)return o=o.error.replace(/.* /,""),console.log("Returned error",o),a(o,"warning")},t="",e=function(e){return a("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:e},error:r,success:function(e){var r;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),a(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),$("#serialized").text(r),e.newkey?a("yourNewTotpKey","warning"):a("yourTotpKey","success"),t=e.token):a("PE24","danger")}})},o=function(){var e=$("#code").val();return e?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:t,code:e,TOTPName:$("#TOTPName").val()},error:r,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?a(e.error,"warning"):a(e.error,"danger"):a("yourKeyIsRegistered","success")}}):a("fillTheForm","warning")};$(document).ready(function(){return e(0),$("#changekey").on("click",function(){return e(1)}),$("#verify").on("click",o)})}).call(this);
(function(){var r,e,n,t,o;n=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},r=function(e,r,t){var o;if(console.log("Error",t),(o=JSON.parse(e.responseText))&&o.error)return o=o.error.replace(/.* /,""),console.log("Returned error",o),n(o,"warning")},t="",e=function(e){return n("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:e},error:r,success:function(e){var r;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),n(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),$("#serialized").text(r),e.newkey?n("yourNewTotpKey","warning"):n("yourTotpKey","success"),t=e.token):n("PE24","danger")}})},o=function(){var e;return(e=$("#code").val())?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:t,code:e,TOTPName:$("#TOTPName").val()},error:r,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?n(e.error,"warning"):n(e.error,"danger"):n("yourKeyIsRegistered","success")}}):n("fillTheForm","warning")},$(document).ready(function(){return e(0),$("#changekey").on("click",function(){return e(1)}),$("#verify").on("click",function(){return o()})})}).call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["totpregistration.js"],"names":["setMsg","msg","level","$","html","window","translate","removeClass","addClass","displayError","j","status","err","res","console","log","JSON","parse","responseText","error","replace","token","getKey","reset","ajax","type","url","portal","dataType","data","newkey","success","s","match","hide","user","secret","show","escape","digits","interval","QRious","element","document","getElementById","value","size","text","verify","val","code","TOTPName","ready","on","call","this"],"mappings":"CAMA,WACE,IAEAA,EAAS,SAASC,EAAKC,GAOrB,OANAC,EAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,4FACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCO,EAAe,SAASC,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GACvBb,EAAOa,EAAK,YAIvBQ,EAAQ,GAERC,EAAS,SAASC,GAEhB,OADAvB,EAAO,cAAe,WACfG,EAAEqB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJC,OAAQP,GAEVJ,MAAOV,EACPsB,QAAS,SAASF,GAChB,IAAQG,EACR,OAAIH,EAAKV,OACHU,EAAKV,MAAMc,MAAM,oBACnB9B,EAAE,cAAc+B,OAEXlC,EAAO6B,EAAKV,MAAO,YAEtBU,EAAKF,QAAUE,EAAKM,MAAQN,EAAKO,QAGvCjC,EAAE,cAAckC,OAChBL,EAAI,kBAAqBM,OAAOT,EAAKF,QAAW,IAAOW,OAAOT,EAAKM,MAAS,WAAaN,EAAKO,OAAS,WAAcE,OAAOT,EAAKF,QAC7G,IAAhBE,EAAKU,SACPP,GAAK,WAAaH,EAAKU,QAEH,KAAlBV,EAAKW,WACPR,GAAK,WAAaH,EAAKW,UAEpB,IAAIC,OAAO,CACdC,QAASC,SAASC,eAAe,MACjCC,MAAOb,EACPc,KAAM,MAER3C,EAAE,eAAe4C,KAAKf,GAClBH,EAAKC,OACP9B,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBqB,EAAQQ,EAAKR,OArBXrB,EAAO,OAAQ,cA0B9BgD,EAAS,WACP,IACAC,EAAM9C,EAAE,SAAS8C,MACjB,OAAKA,EAGI9C,EAAEqB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJR,MAAOA,EACP6B,KAAMD,EACNE,SAAUhD,EAAE,aAAa8C,OAE3B9B,MAAOV,EACPsB,QAAS,SAASF,GAChB,OAAIA,EAAKV,MACHU,EAAKV,MAAMc,MAAM,kBACZjC,EAAO6B,EAAKV,MAAO,WAEnBnB,EAAO6B,EAAKV,MAAO,UAGrBnB,EAAO,sBAAuB,cApBpCA,EAAO,cAAe,YA2BjCG,EAAEwC,UAAUS,MAAM,WAKhB,OAJA9B,EAAO,GACPnB,EAAE,cAAckD,GAAG,QAAS,WAC1B,OAAO/B,EAAO,KAETnB,EAAE,WAAWkD,GAAG,QACdL,OAIVM,KAAKC"}
{"version":3,"sources":["totpregistration.js"],"names":["displayError","getKey","setMsg","token","verify","msg","level","$","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","reset","ajax","type","url","portal","dataType","data","newkey","success","s","match","hide","user","secret","show","escape","digits","interval","QRious","element","document","getElementById","value","size","text","val","code","TOTPName","ready","on","call","this"],"mappings":"CAMA,WACE,IAAIA,EAAcC,EAAQC,EAAQC,EAAOC,EAEzCF,EAAS,SAASG,EAAKC,GAOrB,OANAC,EAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,4FACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCN,EAAe,SAASa,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GACvBd,EAAOc,EAAK,YAIvBb,EAAQ,GAERF,EAAS,SAASuB,GAEhB,OADAtB,EAAO,cAAe,WACfK,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJC,OAAQP,GAEVF,MAAOtB,EACPgC,QAAS,SAASF,GAChB,IAAQG,EACR,OAAIH,EAAKR,OACHQ,EAAKR,MAAMY,MAAM,oBACnB3B,EAAE,cAAc4B,OAEXjC,EAAO4B,EAAKR,MAAO,YAEtBQ,EAAKF,QAAUE,EAAKM,MAAQN,EAAKO,QAGvC9B,EAAE,cAAc+B,OAChBL,EAAI,kBAAqBM,OAAOT,EAAKF,QAAW,IAAOW,OAAOT,EAAKM,MAAS,WAAaN,EAAKO,OAAS,WAAcE,OAAOT,EAAKF,QAC7G,IAAhBE,EAAKU,SACPP,GAAK,WAAaH,EAAKU,QAEH,KAAlBV,EAAKW,WACPR,GAAK,WAAaH,EAAKW,UAEpB,IAAIC,OAAO,CACdC,QAASC,SAASC,eAAe,MACjCC,MAAOb,EACPc,KAAM,MAERxC,EAAE,eAAeyC,KAAKf,GAClBH,EAAKC,OACP7B,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBC,EAAQ2B,EAAK3B,OArBXD,EAAO,OAAQ,cA0B9BE,EAAS,WACP,IAAI6C,EAEJ,OADAA,EAAM1C,EAAE,SAAS0C,OAIR1C,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJ3B,MAAOA,EACP+C,KAAMD,EACNE,SAAU5C,EAAE,aAAa0C,OAE3B3B,MAAOtB,EACPgC,QAAS,SAASF,GAChB,OAAIA,EAAKR,MACHQ,EAAKR,MAAMY,MAAM,kBACZhC,EAAO4B,EAAKR,MAAO,WAEnBpB,EAAO4B,EAAKR,MAAO,UAGrBpB,EAAO,sBAAuB,cApBpCA,EAAO,cAAe,YA2BjCK,EAAEqC,UAAUQ,MAAM,WAKhB,OAJAnD,EAAO,GACPM,EAAE,cAAc8C,GAAG,QAAS,WAC1B,OAAOpD,EAAO,KAETM,EAAE,WAAW8C,GAAG,QAAS,WAC9B,OAAOjD,UAIVkD,KAAKC"}

View File

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

View File

@ -0,0 +1,469 @@
use Test::More;
use strict;
use IO::String;
use JSON qw(to_json from_json);
BEGIN {
require 't/test-lib.pm';
}
my $maintests = 64;
SKIP: {
require Lemonldap::NG::Common::TOTP;
eval { require Crypt::U2F::Server; require Authen::U2F::Tester };
if ( $@ or $Crypt::U2F::Server::VERSION < 0.42 ) {
skip 'Missing U2F libraries', $maintests;
}
eval { require Convert::Base32 };
if ($@) {
skip 'Convert::Base32 is missing';
}
my $res;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
portalMainLogo => 'common/logos/logo_llng_old.png',
contextSwitchingRule => 1,
contextSwitchingStopWithLogout => 0,
contextSwitchingAllowed2fModifications => 1,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
u2fSelfRegistration => 1,
u2fActivation => 1,
}
}
);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# Get Menu
# ------------------------
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectOK($res);
ok(
$res->[2]->[0] =~
m%<span trspan="connectedAs">Connected as</span> rtyler%,
'Connected as rtyler'
) or print STDERR Dumper( $res->[2]->[0] );
expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
## Try to register a TOTP
# TOTP form
my ( $key, $token, $code );
ok(
$res = $client->_get(
'/2fregisters/totp',
cookie => "lemonldap=$id",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# JS query
ok(
$res = $client->_post(
'/2fregisters/totp/getkey', IO::String->new(''),
cookie => "lemonldap=$id",
length => 0,
),
'Get new key'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $key = $res->{secret}, 'Found secret' ) or print STDERR Dumper($res);
ok( $token = $res->{token}, 'Found token' ) or print STDERR Dumper($res);
ok( $res->{user} eq 'rtyler', 'Found user' )
or print STDERR Dumper($res);
$key = Convert::Base32::decode_base32($key);
# Post code
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
'Code' );
ok( $code =~ /^\d{6}$/, 'Code contains 6 digits' );
my $s = "code=$code&token=$token&TOTPName=myTOTP";
ok(
$res = $client->_post(
'/2fregisters/totp/verify',
IO::String->new($s),
length => length($s),
cookie => "lemonldap=$id",
),
'Post code'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $res->{result} == 1, 'TOTP is registered' );
## Try to register an U2F key
ok(
$res = $client->_get(
'/2fregisters/u',
cookie => "lemonldap=$id",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# Ajax registration request
ok(
$res = $client->_post(
'/2fregisters/u/register', IO::String->new(''),
accept => 'application/json',
cookie => "lemonldap=$id",
length => 0,
),
'Get registration challenge'
);
expectOK($res);
my $data;
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( ( $data->{challenge} and $data->{appId} ), ' Get challenge and appId' )
or explain( $data, 'challenge and appId' );
# Build U2F tester
my $tester = Authen::U2F::Tester->new(
certificate => Crypt::OpenSSL::X509->new_from_string(
'-----BEGIN CERTIFICATE-----
MIIB6DCCAY6gAwIBAgIJAJKuutkN2sAfMAoGCCqGSM49BAMCME8xCzAJBgNVBAYT
AlVTMQ4wDAYDVQQIDAVUZXhhczEaMBgGA1UECgwRVW50cnVzdGVkIFUyRiBPcmcx
FDASBgNVBAMMC3ZpcnR1YWwtdTJmMB4XDTE4MDMyODIwMTc1OVoXDTI3MTIyNjIw
MTc1OVowTzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRowGAYDVQQKDBFV
bnRydXN0ZWQgVTJGIE9yZzEUMBIGA1UEAwwLdmlydHVhbC11MmYwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAAQTij+9mI1FJdvKNHLeSQcOW4ob3prvIXuEGJMrQeJF
6OYcgwxrVqsmNMl5w45L7zx8ryovVOti/mtqkh2pQjtpo1MwUTAdBgNVHQ4EFgQU
QXKKf+rrZwA4WXDCU/Vebe4gYXEwHwYDVR0jBBgwFoAUQXKKf+rrZwA4WXDCU/Ve
be4gYXEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiEAiCdOEmw5
hknzHR1FoyFZKRrcJu17a1PGcqTFMJHTC70CIHeCZ8KVuuMIPjoofQd1l1E221rv
RJY1Oz1fUNbrIPsL
-----END CERTIFICATE-----', Crypt::OpenSSL::X509::FORMAT_PEM()
),
key => Crypt::PK::ECC->new(
\'-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOdbZw1swQIL+RZoDQ9zwjWY5UjA1NO81WWjwbmznUbgoAoGCCqGSM49
AwEHoUQDQgAEE4o/vZiNRSXbyjRy3kkHDluKG96a7yF7hBiTK0HiRejmHIMMa1ar
JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
-----END EC PRIVATE KEY-----'
),
);
my $r = $tester->register( $data->{appId}, $data->{challenge} );
ok( $r->is_success, ' Good challenge value' )
or diag( $r->error_message );
my $registrationData = JSON::to_json( {
clientData => $r->client_data,
errorCode => 0,
registrationData => $r->registration_data,
version => "U2F_V2"
}
);
( $host, $url, $query );
$query = Lemonldap::NG::Common::FormEncode::build_urlencoded(
registration => $registrationData,
challenge => $res->[2]->[0],
);
ok(
$res = $client->_post(
'/2fregisters/u/registration', IO::String->new($query),
length => length($query),
accept => 'application/json',
cookie => "lemonldap=$id",
),
'Push registration data'
);
expectOK($res);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( $data->{result} == 1, 'U2F key is registered' )
or explain( $data, '"result":1' );
$client->logout($id);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
( $host, $url, $query ) = expectForm( $res, undef, '/2fchoice', 'token' );
$query .= '&sf=totp';
ok(
$res = $client->_post(
'/2fchoice',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Post TOTP choice'
);
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token' );
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
'Code' );
$query =~ s/code=/code=$code/;
ok(
$res = $client->_post(
'/totp2fcheck', IO::String->new($query),
length => length($query),
),
'Post code'
);
$id = expectCookie($res);
# Get Menu
# ------------------------
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectOK($res);
expectAuthenticatedAs( $res, 'rtyler' );
# Try to switch context 'dwho'
# ContextSwitching form
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'ContextSwitching form',
);
( $host, $url, $query ) =
expectForm( $res, undef, '/switchcontext', 'spoofId' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
'Found trspan="contextSwitching_ON"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
## POST form
$query =~ s/spoofId=/spoofId=dwho/;
ok(
$res = $client->_post(
'/switchcontext',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST switchcontext'
);
expectRedirection( $res, 'http://auth.example.com/' );
my $id2 = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id2",
accept => 'text/html'
),
'Get Menu',
);
expectAuthenticatedAs( $res, 'dwho' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
# 2fregisters
ok(
$res = $client->_get(
'/2fregisters',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form 2fregisters'
);
ok( $res->[2]->[0] =~ /<span id="msg" trspan="choose2f">/,
'Found choose 2F' )
or print STDERR Dumper( $res->[2]->[0] );
my $devices;
ok( $devices = $res->[2]->[0] =~ s%<span device=\'(TOTP|U2F)\' epoch=\'\d{10}\'%%g,
'2F devices found' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $devices == 2, 'two 2F devices found' )
or explain( $devices, 'Two 2F devices registered' );
## Try to register a TOTP
# TOTP form
ok(
$res = $client->_get(
'/2fregisters/totp',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' )
or print STDERR Dumper( $res->[2]->[0] );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# JS query
ok(
$res = $client->_post(
'/2fregisters/totp/getkey', IO::String->new(''),
cookie => "lemonldap=$id2",
length => 0,
),
'Get new key'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $res->{error} eq 'totpExistingKey', 'TOTP already registered' )
or explain( $res, 'Bad result' );
# Try to unregister TOTP
ok(
$res = $client->_post(
'/2fregisters/totp/delete',
IO::String->new("epoch=1234567890"),
length => 16,
cookie => "lemonldap=$id2",
),
'Delete TOTP query'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{error} eq '2FDeviceNotFound', '2F device not found'
) or explain( $data, 'Bad result' );
# Try to verify TOTP
$s = "code=123456&token=1234567890&TOTPName=myTOTP";
ok(
$res = $client->_post(
'/2fregisters/totp/verify',
IO::String->new($s),
length => length($s),
cookie => "lemonldap=$id2",
),
'Post code'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( $data->{error} eq 'PE82', 'PE82' )
or explain( $data, 'Bad result' );
## Try to register an U2F key
# U2F form
ok(
$res = $client->_get(
'/2fregisters/u',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# Ajax registration request
ok(
$res = $client->_post(
'/2fregisters/u/register', IO::String->new(''),
accept => 'application/json',
cookie => "lemonldap=$id2",
length => 0,
),
'Get registration challenge'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{challenge} =~ /\w+/, 'Get challenge'
) or explain( $data, 'Bad result' );
# Try to unregister U2F key
ok(
$res = $client->_post(
'/2fregisters/u/delete',
IO::String->new("epoch=1234567890"),
length => 16,
cookie => "lemonldap=$id2",
),
'Delete U2F key query'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{error} eq '2FDeviceNotFound', '2F device not found'
) or explain( $data, 'Bad result' );
$client->logout($id);
$client->logout($id2);
}
count($maintests);
clean_sessions();
done_testing( count() );

View File

@ -0,0 +1,469 @@
use Test::More;
use strict;
use IO::String;
use JSON qw(to_json from_json);
BEGIN {
require 't/test-lib.pm';
}
my $maintests = 63;
SKIP: {
require Lemonldap::NG::Common::TOTP;
eval { require Crypt::U2F::Server; require Authen::U2F::Tester };
if ( $@ or $Crypt::U2F::Server::VERSION < 0.42 ) {
skip 'Missing U2F libraries', $maintests;
}
eval { require Convert::Base32 };
if ($@) {
skip 'Convert::Base32 is missing';
}
my $res;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
portalMainLogo => 'common/logos/logo_llng_old.png',
contextSwitchingRule => 1,
contextSwitchingStopWithLogout => 0,
contextSwitchingAllowed2fModifications => 0,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
u2fSelfRegistration => 1,
u2fActivation => 1,
}
}
);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# Get Menu
# ------------------------
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectOK($res);
ok(
$res->[2]->[0] =~
m%<span trspan="connectedAs">Connected as</span> rtyler%,
'Connected as rtyler'
) or print STDERR Dumper( $res->[2]->[0] );
expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
## Try to register a TOTP
# TOTP form
my ( $key, $token, $code );
ok(
$res = $client->_get(
'/2fregisters/totp',
cookie => "lemonldap=$id",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# JS query
ok(
$res = $client->_post(
'/2fregisters/totp/getkey', IO::String->new(''),
cookie => "lemonldap=$id",
length => 0,
),
'Get new key'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $key = $res->{secret}, 'Found secret' ) or print STDERR Dumper($res);
ok( $token = $res->{token}, 'Found token' ) or print STDERR Dumper($res);
ok( $res->{user} eq 'rtyler', 'Found user' )
or print STDERR Dumper($res);
$key = Convert::Base32::decode_base32($key);
# Post code
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
'Code' );
ok( $code =~ /^\d{6}$/, 'Code contains 6 digits' );
my $s = "code=$code&token=$token&TOTPName=myTOTP";
ok(
$res = $client->_post(
'/2fregisters/totp/verify',
IO::String->new($s),
length => length($s),
cookie => "lemonldap=$id",
),
'Post code'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $res->{result} == 1, 'TOTP is registered' );
## Try to register an U2F key
ok(
$res = $client->_get(
'/2fregisters/u',
cookie => "lemonldap=$id",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# Ajax registration request
ok(
$res = $client->_post(
'/2fregisters/u/register', IO::String->new(''),
accept => 'application/json',
cookie => "lemonldap=$id",
length => 0,
),
'Get registration challenge'
);
expectOK($res);
my $data;
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( ( $data->{challenge} and $data->{appId} ), ' Get challenge and appId' )
or explain( $data, 'challenge and appId' );
# Build U2F tester
my $tester = Authen::U2F::Tester->new(
certificate => Crypt::OpenSSL::X509->new_from_string(
'-----BEGIN CERTIFICATE-----
MIIB6DCCAY6gAwIBAgIJAJKuutkN2sAfMAoGCCqGSM49BAMCME8xCzAJBgNVBAYT
AlVTMQ4wDAYDVQQIDAVUZXhhczEaMBgGA1UECgwRVW50cnVzdGVkIFUyRiBPcmcx
FDASBgNVBAMMC3ZpcnR1YWwtdTJmMB4XDTE4MDMyODIwMTc1OVoXDTI3MTIyNjIw
MTc1OVowTzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRowGAYDVQQKDBFV
bnRydXN0ZWQgVTJGIE9yZzEUMBIGA1UEAwwLdmlydHVhbC11MmYwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAAQTij+9mI1FJdvKNHLeSQcOW4ob3prvIXuEGJMrQeJF
6OYcgwxrVqsmNMl5w45L7zx8ryovVOti/mtqkh2pQjtpo1MwUTAdBgNVHQ4EFgQU
QXKKf+rrZwA4WXDCU/Vebe4gYXEwHwYDVR0jBBgwFoAUQXKKf+rrZwA4WXDCU/Ve
be4gYXEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiEAiCdOEmw5
hknzHR1FoyFZKRrcJu17a1PGcqTFMJHTC70CIHeCZ8KVuuMIPjoofQd1l1E221rv
RJY1Oz1fUNbrIPsL
-----END CERTIFICATE-----', Crypt::OpenSSL::X509::FORMAT_PEM()
),
key => Crypt::PK::ECC->new(
\'-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOdbZw1swQIL+RZoDQ9zwjWY5UjA1NO81WWjwbmznUbgoAoGCCqGSM49
AwEHoUQDQgAEE4o/vZiNRSXbyjRy3kkHDluKG96a7yF7hBiTK0HiRejmHIMMa1ar
JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
-----END EC PRIVATE KEY-----'
),
);
my $r = $tester->register( $data->{appId}, $data->{challenge} );
ok( $r->is_success, ' Good challenge value' )
or diag( $r->error_message );
my $registrationData = JSON::to_json( {
clientData => $r->client_data,
errorCode => 0,
registrationData => $r->registration_data,
version => "U2F_V2"
}
);
( $host, $url, $query );
$query = Lemonldap::NG::Common::FormEncode::build_urlencoded(
registration => $registrationData,
challenge => $res->[2]->[0],
);
ok(
$res = $client->_post(
'/2fregisters/u/registration', IO::String->new($query),
length => length($query),
accept => 'application/json',
cookie => "lemonldap=$id",
),
'Push registration data'
);
expectOK($res);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( $data->{result} == 1, 'U2F key is registered' )
or explain( $data, '"result":1' );
$client->logout($id);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
( $host, $url, $query ) = expectForm( $res, undef, '/2fchoice', 'token' );
$query .= '&sf=totp';
ok(
$res = $client->_post(
'/2fchoice',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Post TOTP choice'
);
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token' );
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
'Code' );
$query =~ s/code=/code=$code/;
ok(
$res = $client->_post(
'/totp2fcheck', IO::String->new($query),
length => length($query),
),
'Post code'
);
$id = expectCookie($res);
# Get Menu
# ------------------------
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectOK($res);
expectAuthenticatedAs( $res, 'rtyler' );
# Try to switch context 'dwho'
# ContextSwitching form
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'ContextSwitching form',
);
( $host, $url, $query ) =
expectForm( $res, undef, '/switchcontext', 'spoofId' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
'Found trspan="contextSwitching_ON"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
## POST form
$query =~ s/spoofId=/spoofId=dwho/;
ok(
$res = $client->_post(
'/switchcontext',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST switchcontext'
);
expectRedirection( $res, 'http://auth.example.com/' );
my $id2 = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id2",
accept => 'text/html'
),
'Get Menu',
);
expectAuthenticatedAs( $res, 'dwho' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
# 2fregisters
ok(
$res = $client->_get(
'/2fregisters',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form 2fregisters'
);
ok( $res->[2]->[0] =~ /<span id="msg" trspan="choose2f">/,
'Found choose 2F' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] !~ m%<span device=\'(TOTP|U2F)\' epoch=\'\d{10}\'%g,
'No 2F device found' )
or print STDERR Dumper( $res->[2]->[0] );
## Try to register a TOTP
# TOTP form
ok(
$res = $client->_get(
'/2fregisters/totp',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' )
or print STDERR Dumper( $res->[2]->[0] );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# JS query
ok(
$res = $client->_post(
'/2fregisters/totp/getkey', IO::String->new(''),
cookie => "lemonldap=$id2",
length => 0,
),
'Get new key'
);
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
ok( $res->{error} eq 'notAuthorized', 'Not authorized to register a TOTP' )
or explain( $res, 'Bad result' );
# Try to unregister TOTP
ok(
$res = $client->_post(
'/2fregisters/totp/delete',
IO::String->new("epoch=1234567890"),
length => 16,
cookie => "lemonldap=$id2",
),
'Delete TOTP query'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{error} eq 'notAuthorized',
'Not authorized to unregister a TOTP'
) or explain( $data, 'Bad result' );
# Try to verify TOTP
$s = "code=123456&token=1234567890&TOTPName=myTOTP";
ok(
$res = $client->_post(
'/2fregisters/totp/verify',
IO::String->new($s),
length => length($s),
cookie => "lemonldap=$id2",
),
'Post code'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok( $data->{error} eq 'notAuthorized', 'Not authorized to verify a TOTP' )
or explain( $data, 'Bad result' );
## Try to register an U2F key
# U2F form
ok(
$res = $client->_get(
'/2fregisters/u',
cookie => "lemonldap=$id2",
accept => 'text/html',
),
'Form registration'
);
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
ok(
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
# Ajax registration request
ok(
$res = $client->_post(
'/2fregisters/u/register', IO::String->new(''),
accept => 'application/json',
cookie => "lemonldap=$id2",
length => 0,
),
'Get registration challenge'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{error} eq 'notAuthorized',
'Not authorized to register an U2F key'
) or explain( $data, 'Bad result' );
# Try to unregister U2F key
ok(
$res = $client->_post(
'/2fregisters/u/delete',
IO::String->new("epoch=1234567890"),
length => 16,
cookie => "lemonldap=$id2",
),
'Delete U2F key query'
);
eval { $data = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), ' Content is JSON' )
or explain( [ $@, $res->[2] ], 'JSON content' );
ok(
$data->{error} eq 'notAuthorized',
'Not authorized to unregister an U2F key'
) or explain( $data, 'Bad result' );
$client->logout($id);
$client->logout($id2);
}
count($maintests);
clean_sessions();
done_testing( count() );

View File

@ -68,7 +68,7 @@ expectAuthenticatedAs( $res, 'dwho' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as dwho'
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
count(3);

View File

@ -72,7 +72,7 @@ expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as rtyler'
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);

View File

@ -135,7 +135,7 @@ expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as rtyler'
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
count(3);

View File

@ -71,7 +71,7 @@ expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as rtyler'
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);

View File

@ -113,7 +113,7 @@ expectAuthenticatedAs( $res, 'dwho' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as dwho'
'contextSwitching allowed'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);

View File

@ -29,7 +29,6 @@ SKIP: {
impersonationRule => 1,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
totp2fAuthnLevel => 8,
u2fSelfRegistration => 1,
u2fActivation => 1,
}