# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl Lemonldap-NG-Manager.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use Test::More; use Test::Output; use File::Path; use JSON; BEGIN { use_ok('Lemonldap::NG::Common::Session'); use_ok('Lemonldap::NG::Common::CliSessions'); } ######################### SKIP: { eval "use Authen::WebAuthn::Test; use Authen::WebAuthn;"; if ($@) { skip 'Authen::WebAuthn not found'; } my $dir; my $cli; sub setup_sessions { use File::Temp; $dir = File::Temp::tempdir(); my $sessionsdir = "$dir/sessions"; my $psessionsdir = "$dir/psessions"; mkdir $sessionsdir; mkdir $psessionsdir; $cli = Lemonldap::NG::Common::CliSessions->new( conf => { globalStorage => "Apache::Session::File", globalStorageOptions => { Directory => $sessionsdir, LockDirectory => $sessionsdir, }, persistentStorage => "Apache::Session::File", persistentStorageOptions => { Directory => $psessionsdir, LockDirectory => $psessionsdir, }, } ); # Provision test sessions my @psessionsOpts = ( storageModule => "Apache::Session::File", storageModuleOptions => { Directory => $psessionsdir, LockDirectory => $psessionsdir, }, kind => 'Persistent', force => 1, ); #dwho Lemonldap::NG::Common::Session->new( { @psessionsOpts, id => "5efe8af397fc3577e05b483aca964f1b", force => 1, info => { "_2fDevices" => to_json( [ { 'type' => 'UBK', 'epoch' => 1588691690, '_yubikey' => 'cccccceijfnf', 'name' => 'Imported automatically' }, { 'name' => 'U2F-1', 'type' => 'U2F', 'epoch' => 1588691728, '_keyHandle' => '4aS6vXlFQpG5XZSoad6auM9fFu7Q1wazQYwfPtPKN_Hll6Up_ceeWkOgqxm49swWq4Vvcg5UlX0sQQhuRe8heA', '_userKey' => 'BMgMqKPL2PhsjCNW78UEQyNF8zlJtrAAPtWMUDBp9VfDRF5oL2xkwFuyXRMPtRZ7lNfGijDrMc06bDNfp478sQQ', }, { 'name' => 'U2F-2', 'type' => 'U2F', 'epoch' => 1588691730, '_keyHandle' => 'F1Kk9V_O7KDPIx-mqp6CIjbz7ljA-ihWVWyoP1xYBe_HPLHR74aTLanmn0b4vI8DumiBWO1DAle3k6N55cXreg', '_userKey' => 'BAE_svIcxLfm2Knart7DI1ScfBnCt-OFKDWugp3YMO14tamwuc_wN0vSh1D_0DV4Ao3S5GNQZXxtjtUADHTwXHA', }, { _credentialId => 'noconflict', 'name' => 'Existing WebAuthn', 'type' => 'WebAuthn', 'epoch' => 1588691798 } ] ), "_oidcConsents" => to_json( [ { 'scope' => 'openid email', 'rp' => 'rp-example', 'epoch' => 1589288341 }, { 'scope' => 'openid email', 'epoch' => 1589291482, 'rp' => 'rp-example2' } ] ), "_session_uid" => "dwho", } } ); # rtyler Lemonldap::NG::Common::Session->new( { @psessionsOpts, id => "8d3bc3b0e14ea2a155f275aa7c07ebee", force => 1, info => { "_session_uid" => "rtyler", "_2fDevices" => to_json( [ { 'type' => 'UBK', 'epoch' => 1588691690, '_yubikey' => 'cccccceijfnf', 'name' => 'Imported automatically' }, { 'name' => 'U2F-3', 'type' => 'U2F', 'epoch' => 1588691734, '_keyHandle' => '4suXv5Cf10vbJEP72mVkLpBjhSqy5niOgfc0X_MjdxZ_g2e-V8biC6WyCTpF_kGV1FCa06YlcryPCtWUuUST_g', '_userKey' => 'BIXrgc12iGGOYIGyWKd8WeOGCKyTkFA7jXkjlLS0i1MA3vy8gDocfqYCngXMzBAmtGI7FfMlbkG6DJeSubdxAVc', }, ] ), } } ); } sub getJson { my @args = @_; my ($str) = Test::Output::output_from( sub { $cli->run(@args); } ); return from_json($str); } sub getLines { my @args = @_; my ($str) = Test::Output::output_from( sub { $cli->run(@args); } ); return [ split /\n/, $str ]; } setup_sessions(); my $res; # Migrate U2F $cli->run( "secondfactors", {}, "migrateu2f", "dwho" ); $res = getJson( "secondfactors", {}, "get", "rtyler" ); is( values %{$res}, 2, "Still 2 devices" ); is( ( grep { $_->{type} eq "WebAuthn" } values %{$res} ), 0, "No WebAuthn sessions created" ); $res = getJson( "secondfactors", {}, "get", "dwho" ); is( values %{$res}, 6, "Expect 6 devices after migration" ); is( ( grep { $_->{type} eq "U2F" } values %{$res} ), 2, "U2F still present" ); is( ( grep { $_->{type} eq "UBK" } values %{$res} ), 1, "UBK still in place" ); is( ( grep { $_->{type} eq "WebAuthn" } values %{$res} ), 3, "New WebAuthn device" ); my $migratedid = "MTU4ODY5MTcyODo6V2ViQXV0aG46OlUyRi0x"; is( $res->{$migratedid}->{_signCount}, 0, "migrated signcount" ); is( $res->{$migratedid}->{_credentialId}, '4aS6vXlFQpG5XZSoad6auM9fFu7Q1wazQYwfPtPKN_Hll6Up_ceeWkOgqxm49swWq4Vvcg5UlX0sQQhuRe8heA', "migrated credential ID" ); is( $res->{$migratedid}->{_credentialPublicKey}, 'pQECAyYgASFYIMgMqKPL2PhsjCNW78UEQyNF8zlJtrAAPtWMUDBp9VfDIlggRF5oL2xkwFuyXRMPtRZ7lNfGijDrMc06bDNfp478sQQ', "migrated credential key" ); is( $res->{$migratedid}->{epoch}, '1588691728', "migrated epoch" ); is( $res->{$migratedid}->{name}, "U2F-1", "migrated name" ); # Check idempotence $cli->run( "secondfactors", {}, "migrateu2f", "dwho" ); $res = getJson( "secondfactors", {}, "get", "dwho" ); is( values %{$res}, 6, "Expect still 6 devices after rerunning migration" ); is( ( grep { $_->{type} eq "U2F" } values %{$res} ), 2, "U2F still in place" ); is( ( grep { $_->{type} eq "UBK" } values %{$res} ), 1, "UBK still in place" ); is( ( grep { $_->{type} eq "WebAuthn" } values %{$res} ), 3, "Same WebAuthn devices" ); rmtree $dir; setup_sessions(); # Migrate all $cli->run( "secondfactors", { all => 1 }, "migrateu2f" ); $res = getJson( "secondfactors", {}, "get", "dwho" ); is( values %{$res}, 6, "Expect 6 devices after migration" ); is( ( grep { $_->{type} eq "U2F" } values %{$res} ), 2, "U2F still in place" ); is( ( grep { $_->{type} eq "UBK" } values %{$res} ), 1, "UBK still in place" ); is( ( grep { $_->{type} eq "WebAuthn" } values %{$res} ), 3, "New WebAuthn device" ); $res = getJson( "secondfactors", {}, "get", "rtyler" ); is( values %{$res}, 3, "Expect 3 devices after migration" ); is( ( grep { $_->{type} eq "U2F" } values %{$res} ), 1, "U2F still in place" ); is( ( grep { $_->{type} eq "UBK" } values %{$res} ), 1, "UBK still in place" ); is( ( grep { $_->{type} eq "WebAuthn" } values %{$res} ), 1, "New WebAuthn device" ); rmtree $dir; } done_testing();