Add WebAuthn to manager 2FA (#1411)

This commit is contained in:
Maxime Besson 2021-12-13 18:52:00 +01:00
parent ca0bc3422d
commit 38a100f6f6
12 changed files with 31 additions and 24 deletions

View File

@ -15,6 +15,7 @@ extends qw(
Lemonldap::NG::Common::Conf::AccessLib
);
use constant _2FTYPES => [ "UBK", "U2F", "TOTP", "WebAuthn" ];
our $VERSION = '2.0.10';
#############################
@ -46,7 +47,7 @@ sub init {
$self->{hiddenAttributes} //= "_password";
$self->{hiddenAttributes} .= ' _session_id'
unless $conf->{displaySessionId};
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1';
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = $self->{WebAuthnCheck} = '1';
return 1;
}
@ -67,7 +68,7 @@ sub del2F {
my $epoch = $params->{epoch}
or return $self->sendError( $req, 'Missing "epoch" parameter', 400 );
if ( $type =~ /\b(?:U2F|TOTP|UBK)\b/ ) {
if ( grep { $_ eq $type } @{_2FTYPES()} ) {
$self->logger->debug(
"Call procedure delete2F with type=$type and epoch=$epoch");
return $self->delete2F( $req, $session, $skey );
@ -117,7 +118,7 @@ sub sfa {
$moduleOptions->{backend} = $mod->{module};
# Select 2FA sessions to display
foreach (qw(U2F TOTP UBK)) {
foreach (@{_2FTYPES()}) {
$self->{ $_ . 'Check' } = delete $params->{ $_ . 'Check' }
if ( defined $params->{ $_ . 'Check' } );
}
@ -188,17 +189,18 @@ sub sfa {
# Remove sessions without at least one 2F device(s)
$self->logger->debug(
"Removing sessions without at least one 2F device(s)...");
my $_2f_types_re = join ('|', @{_2FTYPES()});
foreach my $session ( keys %$res ) {
delete $res->{$session}
unless ( defined $res->{$session}->{_2fDevices}
and $res->{$session}->{_2fDevices} =~
/"type":\s*"(?:U2F|TOTP|UBK)"/s );
/"type":\s*"(?:$_2f_types_re)"/s );
}
# Filter 2FA sessions if needed
$self->logger->debug("Filtering 2F sessions...");
my $all = ( keys %$res );
foreach (qw(U2F TOTP UBK)) {
foreach (@{_2FTYPES()}) {
if ( $self->{ $_ . 'Check' } eq '2' ) {
foreach my $session ( keys %$res ) {
delete $res->{$session}

View File

@ -630,7 +630,7 @@ sub tests {
my $msg = '';
my $ok = 0;
foreach (qw(u totp yubikey)) {
foreach (qw(u totp yubikey webauthn)) {
$ok ||= $conf->{ $_ . '2fActivation' }
&& $conf->{ $_ . '2fSelfRegistration' };
last if ($ok);

View File

@ -73,6 +73,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
$scope.U2FCheck = "1"
$scope.TOTPCheck = "1"
$scope.UBKCheck = "1"
$scope.WebAuthnCheck = "1"
# Import translations functions
$scope.translateP = $translator.translateP
@ -201,7 +202,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
subres = []
for attr in attrs
if session[attr]
if session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)
if session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK|WebAuthn)"/)
subres.push
title: "type"
value: "name"
@ -295,7 +296,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
over = 0
# Launch HTTP query
$http.get("#{scriptname}sfa/#{sessionType}?#{query}&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}&UBKCheck=#{$scope.UBKCheck}").then (response) ->
$http.get("#{scriptname}sfa/#{sessionType}?#{query}&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}&UBKCheck=#{$scope.UBKCheck}&WebAuthnCheck=#{$scope.WebAuthnCheck}").then (response) ->
data = response.data
if data.result
for n in data.values
@ -346,7 +347,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
over = 0
# Launch HTTP
$http.get("#{scriptname}sfa/#{sessionType}?_session_uid=#{$scope.searchString}*&groupBy=substr(_session_uid,#{$scope.searchString.length})&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}&UBKCheck=#{$scope.UBKCheck}").then (response) ->
$http.get("#{scriptname}sfa/#{sessionType}?_session_uid=#{$scope.searchString}*&groupBy=substr(_session_uid,#{$scope.searchString.length})&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}&UBKCheck=#{$scope.UBKCheck}&WebAuthnCheck=#{$scope.WebAuthnCheck}").then (response) ->
data = response.data
if data.result
for n in data.values

View File

@ -246,7 +246,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
subres = []
for attr in attrs
if session[attr]
if session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)
if session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK|WebAuthn)"/)
subres.push
title: "type"
value: "name"

View File

@ -89,6 +89,7 @@
$scope.U2FCheck = "1";
$scope.TOTPCheck = "1";
$scope.UBKCheck = "1";
$scope.WebAuthnCheck = "1";
$scope.translateP = $translator.translateP;
$scope.translate = $translator.translate;
$scope.translateTitle = function(node) {
@ -205,7 +206,7 @@
for (i = 0, len = attrs.length; i < len; i++) {
attr = attrs[i];
if (session[attr]) {
if (session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)) {
if (session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK|WebAuthn)"/)) {
subres.push({
title: "type",
value: "name",
@ -303,7 +304,7 @@
} else {
over = 0;
}
return $http.get(scriptname + "sfa/" + sessionType + "?" + query + "&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck + "&UBKCheck=" + $scope.UBKCheck).then(function(response) {
return $http.get(scriptname + "sfa/" + sessionType + "?" + query + "&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck + "&UBKCheck=" + $scope.UBKCheck + "&WebAuthnCheck=" + $scope.WebAuthnCheck).then(function(response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {
@ -345,7 +346,7 @@
} else {
over = 0;
}
return $http.get(scriptname + "sfa/" + sessionType + "?_session_uid=" + $scope.searchString + "*&groupBy=substr(_session_uid," + $scope.searchString.length + ")&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck + "&UBKCheck=" + $scope.UBKCheck).then(function(response) {
return $http.get(scriptname + "sfa/" + sessionType + "?_session_uid=" + $scope.searchString + "*&groupBy=substr(_session_uid," + $scope.searchString.length + ")&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck + "&UBKCheck=" + $scope.UBKCheck + "&WebAuthnCheck=" + $scope.WebAuthnCheck).then(function(response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -280,7 +280,7 @@
for (i = 0, len = attrs.length; i < len; i++) {
attr = attrs[i];
if (session[attr]) {
if (session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)) {
if (session[attr].toString().match(/"type":\s*"(?:TOTP|U2F|UBK|WebAuthn)"/)) {
subres.push({
title: "type",
value: "name",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -22,6 +22,9 @@
&nbsp;&nbsp;&&nbsp;&nbsp;
<input type="checkbox" ng-model="UBKCheck" class="form-check-input" ng-true-value="2" ng-false-value="1" ng-change="search2FA()"/>
<label class="form-check-label" for="UBKCheck">UBK</label>
&nbsp;&nbsp;&&nbsp;&nbsp;
<input type="checkbox" ng-model="WebAuthnCheck" class="form-check-input" ng-true-value="2" ng-false-value="1" ng-change="search2FA()"/>
<label class="form-check-label" for="WebAuthnCheck">WebAuthn</label>
</div>
</form>
</ul>
@ -104,16 +107,16 @@
</table>
</div>
<div ng-if="!node.nodes" >
<th class="col-md-3" ng-if="node.title!='UBK' && node.title!='TOTP' && node.title!='U2F'">{{translate(node.title)}}</th>
<td class="data-{{node.epoch}}" ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'" >{{node.title}}</td>
<th class="col-md-3" ng-if="node.title!='UBK' && node.title!='TOTP' && node.title!='U2F' && node.title!='WebAuthn'">{{translate(node.title)}}</th>
<td class="data-{{node.epoch}}" ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' || node.title=='WebAuthn'" >{{node.title}}</td>
<th class="col-md-3" ng-if="node.title=='type'">{{translate(node.value)}}</th>
<td class="col-md-3 data-{{node.epoch}}" ng-if="node.title!='type'" >{{node.value}}</td>
<th class="col-md-3" ng-if="node.title=='type'">{{translate(node.epoch)}}</th>
<td class="col-md-3 data-{{node.epoch}}" ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'">{{localeDate(node.epoch)}}</td>
<td class="col-md-3 data-{{node.epoch}}" ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' || node.title=='WebAuthn'">{{localeDate(node.epoch)}}</td>
<td class="data-{{node.epoch}}">
<span ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="delete2FA(node.title, node.epoch)"></span>
<span ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' || node.title=='WebAuthn'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="delete2FA(node.title, node.epoch)"></span>
<!--
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' || node.title=='WebAuthn' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
-->
</td>
</div>

View File

@ -108,7 +108,7 @@
<td class="data-{{node.epoch}}">
<span ng-if="node.td=='2'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="deleteOIDCConsent(node.title, node.epoch)"></span>
<!--
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' || node.title=='WebAuthn' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
-->
</td>
</div>