commit
4583f3a9e6
@ -0,0 +1,46 @@ |
||||
Adaptative Authentication Level |
||||
=============================== |
||||
|
||||
Presentation |
||||
------------ |
||||
|
||||
A user obtain an authentication level depending on which authentication |
||||
module was used, and eventually which second factor module. |
||||
|
||||
This plugin allows to adapt this authentication level depending on |
||||
other conditions, like network, device, etc. |
||||
|
||||
Sample use case: a strategic application is configured to require an |
||||
authentication level of 5. Users obtain level 2 with their login/password |
||||
and level 5 using a TOTP second factor. You can trust users form internal |
||||
network by incrementing their authentication level based on their IP address, |
||||
they would then not be forced to use 2FA to access the strategic application. |
||||
|
||||
.. tip:: |
||||
|
||||
This use case works if you enable the *Use 2FA for session upgrade* option. |
||||
|
||||
Configuration |
||||
------------- |
||||
|
||||
This plugin is enabled when at least one rule is defind. |
||||
|
||||
To configure rules, go in ``General Parameters`` > ``Plugins`` > |
||||
``Adapative Authentication Level``. |
||||
|
||||
You can then create rules with these fields: |
||||
|
||||
- **Rule**: The condition that will be evaluated. If this condition |
||||
does not return true, then the level is not changed. |
||||
- **Value**: How change the authentication level. First character is |
||||
``+``, ``-`` or ``=``, the second part is the number to add, remove |
||||
or set. |
||||
|
||||
|
||||
.. tip:: |
||||
|
||||
By example, to add 3 to authentication level for users from 192.168.0.0/24 network: |
||||
|
||||
- Rule: ``$env->{REMOTE_ADDR} =~ /^192\.168\./`` |
||||
- Value: ``+3`` |
||||
|
@ -0,0 +1,27 @@ |
||||
InWebo Second Factor |
||||
==================== |
||||
|
||||
`InWebo <https://www.inwebo.com/>`_ is a proprietary MFA solution. |
||||
You can use is as second factor trough :doc:`Radius 2FA module<radius2f>`. |
||||
|
||||
Configuration |
||||
~~~~~~~~~~~~~ |
||||
|
||||
On InWebo side : |
||||
|
||||
- Create a connector of type ``Radius Push``. |
||||
- Fill in the “IP Address” field with the IP of the public interface of your LL::NG server. |
||||
- Enter a secret, that you will also configure on LL::NG side. |
||||
|
||||
See `InWebo Radius documentation <https://inwebo.atlassian.net/wiki/spaces/DOCS/pages/2216886275/RADIUS+integration+and+redundancy>`_ for more details. |
||||
|
||||
On LL::NG side, go in "General Parameters » Second factors » |
||||
Radius second factor". |
||||
|
||||
- **Activation**: Set to ``On`` to activate this module, or use a |
||||
specific rule to select which users may use this type of second |
||||
factor |
||||
- **Server hostname**: The hostname of InWebo Radius server (for example `radius2.myinwebo.com`) |
||||
- **Shared secret**: The secret key declared on InWebo side |
||||
|
||||
See :doc:`Radius 2FA module<radius2f>` for more details. |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,83 @@ |
||||
package Lemonldap::NG::Portal::Plugins::AdaptativeAuthenticationLevel; |
||||
|
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_OK |
||||
); |
||||
|
||||
our $VERSION = '2.0.10'; |
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin'; |
||||
|
||||
use constant afterData => 'adaptAuthenticationLevel'; |
||||
|
||||
has rules => ( is => 'rw', default => sub { {} } ); |
||||
|
||||
sub init { |
||||
my ($self) = @_; |
||||
$self->logger->debug('Init AdaptativeAuthenticationLevel plugin'); |
||||
|
||||
foreach ( |
||||
keys %{ $self->conf->{adaptativeAuthenticationLevelRules} // {} } ) |
||||
{ |
||||
$self->logger->debug("adaptativeAuthenticationLevelRules key -> $_"); |
||||
$self->logger->debug( "adaptativeAuthenticationLevelRules value -> " |
||||
. $self->conf->{adaptativeAuthenticationLevelRules}->{$_} ); |
||||
|
||||
my $rule = |
||||
$self->p->buildRule( $_, 'adaptativeAuthenticationLevelRules' ); |
||||
next unless $rule; |
||||
$self->rules->{$_} = $rule; |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
sub adaptAuthenticationLevel { |
||||
my ( $self, $req ) = @_; |
||||
|
||||
my $userid = $req->sessionInfo->{ $self->conf->{whatToTrace} }; |
||||
$self->logger->debug("Check adaptative authentication rules for $userid"); |
||||
|
||||
my $authenticationLevel = $req->sessionInfo->{authenticationLevel}; |
||||
$self->logger->debug( |
||||
"Current authentication level for $userid is $authenticationLevel"); |
||||
|
||||
my $updatedAuthenticationLevel = $authenticationLevel; |
||||
|
||||
foreach ( keys %{ $self->rules } ) { |
||||
my $rule = $_; |
||||
$self->logger->debug( |
||||
"Check adaptativeAuthenticationLevelRules -> $rule"); |
||||
if ( $self->rules->{$_}->( $req, $req->sessionInfo ) ) { |
||||
my $levelOperation = |
||||
$self->conf->{adaptativeAuthenticationLevelRules}->{$_}; |
||||
$self->logger->debug( |
||||
"User $userid match rule, apply $levelOperation on authentication level" |
||||
); |
||||
|
||||
my ( $op, $level ) = ( $levelOperation =~ /([=+-])?(\d+)/ ); |
||||
$updatedAuthenticationLevel = $level if ( !$op or $op eq '=' ); |
||||
$updatedAuthenticationLevel += $level if ( $op and $op eq '+' ); |
||||
$updatedAuthenticationLevel -= $level if ( $op and $op eq '-' ); |
||||
$self->logger->debug( |
||||
"Authentication level for $userid is now $updatedAuthenticationLevel" |
||||
); |
||||
} |
||||
} |
||||
|
||||
if ( $authenticationLevel ne $updatedAuthenticationLevel ) { |
||||
$self->logger->debug( |
||||
"Authentication level has changed for $userid, update session"); |
||||
$self->p->updateSession( |
||||
$req, |
||||
{ |
||||
'authenticationLevel' => $updatedAuthenticationLevel |
||||
} |
||||
); |
||||
|
||||
} |
||||
|
||||
return PE_OK; |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,84 @@ |
||||
use Test::More; |
||||
use strict; |
||||
use IO::String; |
||||
use Data::Dumper; |
||||
|
||||
BEGIN { |
||||
require 't/test-lib.pm'; |
||||
} |
||||
|
||||
my $res; |
||||
my $id; |
||||
my $json; |
||||
|
||||
my $client = LLNG::Manager::Test->new( { |
||||
ini => { |
||||
logLevel => 'error', |
||||
authentication => 'Demo', |
||||
userDB => 'Same', |
||||
adaptativeAuthenticationLevelRules => { |
||||
'$uid eq "dwho"' => '+2', |
||||
'$uid eq "msmith"' => '=5', |
||||
}, |
||||
restSessionServer => 1, |
||||
} |
||||
} |
||||
); |
||||
|
||||
ok( |
||||
$res = $client->_post( |
||||
'/', |
||||
IO::String->new('user=dwho&password=dwho'), |
||||
accept => 'text/html', |
||||
length => 23 |
||||
), |
||||
'Auth query' |
||||
); |
||||
count(1); |
||||
$id = expectCookie($res); |
||||
|
||||
ok( |
||||
$res = $client->_get( |
||||
'/session/my/global', cookie => "lemonldap=$id" |
||||
), |
||||
'Get session' |
||||
); |
||||
count(1); |
||||
$json = expectJSON($res); |
||||
|
||||
ok( $json->{authenticationLevel} == 3, 'Authentication level upgraded' ); |
||||
count(1); |
||||
|
||||
ok( $client->logout($id), 'Logout' ); |
||||
count(1); |
||||
|
||||
ok( |
||||
$res = $client->_post( |
||||
'/', |
||||
IO::String->new('user=msmith&password=msmith'), |
||||
accept => 'text/html', |
||||
length => 27 |
||||
), |
||||
'Auth query' |
||||
); |
||||
count(1); |
||||
$id = expectCookie($res); |
||||
|
||||
ok( |
||||
$res = $client->_get( |
||||
'/session/my/global', cookie => "lemonldap=$id" |
||||
), |
||||
'Get session' |
||||
); |
||||
count(1); |
||||
$json = expectJSON($res); |
||||
|
||||
ok( $json->{authenticationLevel} == 5, 'Authentication level upgraded' ); |
||||
count(1); |
||||
|
||||
ok( $client->logout($id), 'Logout' ); |
||||
count(1); |
||||
|
||||
clean_sessions(); |
||||
|
||||
done_testing( count() ); |
@ -0,0 +1,408 @@ |
||||
use Test::More; |
||||
use strict; |
||||
use IO::String; |
||||
|
||||
BEGIN { |
||||
require 't/test-lib.pm'; |
||||
} |
||||
my $maintests = 57; |
||||
|
||||
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'; |
||||
} |
||||
use_ok('Lemonldap::NG::Common::FormEncode'); |
||||
|
||||
my $res; |
||||
my $client = LLNG::Manager::Test->new( { |
||||
ini => { |
||||
logLevel => 'error', |
||||
authentication => 'Demo', |
||||
userDB => 'Same', |
||||
portalMainLogo => 'common/logos/logo_llng_old.png', |
||||
impersonationRule => 1, |
||||
totp2fSelfRegistration => 1, |
||||
totp2fActivation => 1, |
||||
totp2fAuthnLevel => 8, |
||||
u2fSelfRegistration => 1, |
||||
u2fActivation => 1, |
||||
} |
||||
} |
||||
); |
||||
|
||||
# Try to authenticate |
||||
# ------------------- |
||||
ok( |
||||
$res = $client->_post( |
||||
'/', IO::String->new('user=rtyler&password=rtyler'), |
||||
length => 27 |
||||
), |
||||
'Auth query' |
||||
); |
||||
my $id = expectCookie($res); |
||||
|
||||
ok( |
||||
$res = $client->_get( |
||||
'/', |
||||
cookie => "lemonldap=$id", |
||||
accept => 'text/html' |
||||
), |
||||
'Get Menu' |
||||
); |
||||
expectAuthenticatedAs( $res, 'rtyler' ); |
||||
ok( $res->[2]->[0] =~ m%<span trspan="sfaManager">sfaManager</span>%, |
||||
'sfaManager link found' ) |
||||
or print STDERR Dumper( $res->[2]->[0] ); |
||||
|
||||
## Try to register a TOTP |
||||
# TOTP form |
||||
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' ); |
||||
my ( $key, $token ); |
||||
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 |
||||
my $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" |
||||
} |
||||
); |
||||
my ( $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 impersonate |
||||
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', ); |
||||
( $host, $url, $query ) = |
||||
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' ); |
||||
|
||||
$query =~ s/user=/user=rtyler/; |
||||
$query =~ s/password=/password=rtyler/; |
||||
$query =~ s/spoofId=/spoofId=dwho/; |
||||
|
||||
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, 'dwho' ); |
||||
|
||||
# 2fregisters |
||||
ok( |
||||
$res = $client->_get( |
||||
'/2fregisters', |
||||
cookie => "lemonldap=$id", |
||||
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=$id", |
||||
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=$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( $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=$id", |
||||
), |
||||
'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=$id", |
||||
), |
||||
'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=$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' |
||||
); |
||||
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=$id", |
||||
), |
||||
'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); |
||||
} |
||||
|
||||
count($maintests); |
||||
clean_sessions(); |
||||
done_testing( count() ); |
Loading…
Reference in new issue