MDL-79920 tool_mfa: Tests for new MFA methods and layout

This commit is contained in:
David Woloszyn 2024-03-04 17:05:31 +11:00
parent c415eed491
commit 5662d6a619
5 changed files with 256 additions and 0 deletions

View File

@ -0,0 +1,69 @@
@tool @tool_mfa
Feature: Set up and manage user factors
In order to set up or manage my user factor
As a user
I need to configure the user factor settings in my preferences
Background:
Given I log in as "admin"
And the following config values are set as admin:
| enabled | 1 | tool_mfa |
Scenario: I see the correct buttons for factor setup and management displayed
Given the following config values are set as admin:
| enabled | 1 | factor_email |
And the following config values are set as admin:
| enabled | 1 | factor_webauthn |
And the following config values are set as admin:
| enabled | 1 | factor_totp |
And the following "tool_mfa > User factors" exist:
| username | factor | label |
| admin | email | test@test.com |
| admin | webauthn | MacBook |
And I follow "Preferences" in the user menu
When I click on "Multi-factor authentication preferences" "link"
# This is the only factor not yet set up.
Then I should not see "Active" in the "#factor-card-totp" "css_element"
# The following factors are already set up.
And I should see "Active" in the "#factor-card-email" "css_element"
And I should see "Active" in the "#factor-card-webauthn" "css_element"
And I click on "Set up authenticator app" "button"
And I should see "Set up authenticator app"
And I click on "Cancel" "button"
And I click on "Manage security key" "button"
And I should see "Manage security key"
@javascript
Scenario: I can revoke a factor only when there is more than one active factor
Given the following config values are set as admin:
| enabled | 1 | factor_webauthn |
And the following config values are set as admin:
| enabled | 1 | factor_sms |
And the following "tool_mfa > User factors" exist:
| username | factor | label |
| admin | sms | +409111222 |
| admin | webauthn | MacBook |
And I follow "Preferences" in the user menu
And I click on "Multi-factor authentication preferences" "link"
And I click on "Manage SMS" "button"
And I click on "Remove" "button" in the "+409111222" "table_row"
When I click on "Yes, remove" "button" in the "Remove '+409111222' SMS?" "dialogue"
Then I should see "'SMS mobile phone - +409111222' successfully removed"
# Now there is only one active factor left.
And I click on "Manage security key" "button"
And I should see "Replace" in the "MacBook" "table_row"
And I should not see "Remove" in the "MacBook" "table_row"
@javascript
Scenario: I can replace a factor
Given the following config values are set as admin:
| enabled | 1 | factor_webauthn |
And the following "tool_mfa > User factors" exist:
| username | factor | label |
| admin | webauthn | MacBook |
And I follow "Preferences" in the user menu
And I click on "Multi-factor authentication preferences" "link"
And I click on "Manage security key" "button"
And I click on "Replace" "button" in the "MacBook" "table_row"
When I click on "Yes, replace" "button" in the "Replace 'MacBook' security key?" "dialogue"
Then I should see "Replace security key"

View File

@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
/**
* Behat data generator for tool_mfa.
*
* @package tool_mfa
* @category test
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_tool_mfa_generator extends behat_generator_base {
/**
* Get the list of creatable entities for a tool_mfa.
*
* @return array
*/
protected function get_creatable_entities(): array {
return [
'User factors' => [
'singular' => 'User factor',
'datagenerator' => 'user_factors',
'required' => [
'username',
'factor',
'label',
],
],
];
}
}

View File

@ -0,0 +1,64 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/../../lib.php');
/**
* Data generator for tool_mfa plugin.
*
* @package tool_mfa
* @category test
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_mfa_generator extends component_generator_base {
/**
* Create user factors.
*
* @param array $record
* @return stdClass
*/
public function create_user_factors(array $record): \stdClass {
global $DB;
$factorobject = \tool_mfa\plugininfo\factor::get_factor($record['factor']);
if (!$factorobject) {
throw new coding_exception('Unknown factor supplied.');
}
$user = $DB->get_record('user', ['username' => $record['username']]);
if (!$user) {
throw new coding_exception('No user found with that username.');
}
$record = (object) array_merge([
'userid' => $user->id,
'secret' => '555553',
'timecreated' => time() - DAYSECS,
'createdfromip' => '0:0:0:0:0:0:0:1',
'timemodified' => time() - MINSECS,
'lastverified' => time(),
'revoked' => 0,
'lockcounter' => 0,
], $record);
$record->id = $DB->insert_record('tool_mfa', $record);
return $record;
}
}

View File

@ -82,4 +82,37 @@ class object_factor_base_test extends \advanced_testcase {
$this->assertTrue($totpfactor->revoke_user_factor($factorinstance2->id));
$this->assertEquals(0, count($totpfactor->get_active_user_factors($user)));
}
/**
* Tests the replacement of a factor.
*
* @covers ::setup_user_factor
* @covers ::replace_user_factor
*/
public function test_replace_user_factor(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$factor = \tool_mfa\plugininfo\factor::get_factor('totp');
// Set up the factor.
$data1 = new \stdClass();
$data1->secret = 'fakesecret1';
$data1->devicename = 'fakedevice1';
$factor1 = $factor->setup_user_factor($data1);
// Prepare some replacement data.
$data2 = new \stdClass();
$data2->secret = 'fakesecret2';
$data2->devicename = 'fakedevice2';
// Replace the active factor with the replacement data.
$factor2 = $factor->replace_user_factor($data2, $factor1->id);
// Check the active factor is the newer one.
$activefactors = $factor->get_active_user_factors($user);
$this->assertEquals(1, count($activefactors));
$this->assertEquals($factor2->id, $activefactors[0]->id);
}
}

View File

@ -75,4 +75,48 @@ class plugininfo_factor_test extends \advanced_testcase {
$this->assertEquals(2, count(\tool_mfa\plugininfo\factor::get_active_user_factor_types()));
$this->assertEquals('fallback', \tool_mfa\plugininfo\factor::get_next_user_login_factor()->name);
}
/**
* Tests if a user has more than one active factor.
*
* @covers ::user_has_more_than_one_active_factors
*/
public function test_user_has_more_than_one_active_factors(): void {
global $DB;
$this->resetAfterTest(true);
// Create a user.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Create two active user factors.
set_config('enabled', 1, 'factor_totp');
set_config('enabled', 1, 'factor_webauthn');
$data = new \stdClass();
$data->userid = $user->id;
$data->factor = 'totp';
$data->label = 'testtotp';
$data->revoked = 0;
$DB->insert_record('tool_mfa', $data);
$data = new \stdClass();
$data->userid = $user->id;
$data->factor = 'webauthn';
$data->label = 'testwebauthn';
$data->revoked = 0;
$factorid = $DB->insert_record('tool_mfa', $data);
// Test there is more than one active factor.
$hasmorethanonefactor = \tool_mfa\plugininfo\factor::user_has_more_than_one_active_factors();
$this->assertTrue($hasmorethanonefactor);
// Revoke a factor.
$DB->set_field('tool_mfa', 'revoked', 1, ['id' => $factorid]);
// There should no longer be more than one active factor.
$hasmorethanonefactor = \tool_mfa\plugininfo\factor::user_has_more_than_one_active_factors();
$this->assertFalse($hasmorethanonefactor);
}
}