MDL-78509 tool_mfa: Fixes based on the report and other issues

In this commit, there are couple of fixes based on the report:
1. Removed legacy polyfill from provider.php
2. Fixed phpunit warning detected by CodeChecker
3. Removed unused files
4. Fixed the PHPunit failures by removing "securityquestions"
   from the data_provider due to it has not been included as one of the factors
5. Added PHPunit test to the factors that can be unittested
6. Removed !important rule from tool_mfa/styles.css
7. Added (int) type to sleep method within sleep_timer method due to a php deprecation
in too_mfa/classes/manager.php
8. Changed last param form bool to string in not_enough_factors() when initiating
a new single_button object in tool_mfa/renderer.php
9. Add explanation text to login page
10. Fixed "Access to an undefined property .." from PHPStan
12. Fixed all the "Variable $.. might not be defined" from PHPStan
13. Fixed the issue from https://github.com/catalyst/moodle-tool_mfa/issues/379
This commit is contained in:
Stevani Andolo
2023-08-29 11:06:44 +08:00
parent 63d8cfcf87
commit 00db83d1bf
110 changed files with 1331 additions and 1036 deletions
-15
View File
@@ -1,15 +0,0 @@
# .github/workflows/ci.yml
name: ci
on: [push, pull_request]
jobs:
test:
uses: catalyst/catalyst-moodle-workflows/.github/workflows/ci.yml@main
secrets:
# Required if you plan to publish (uncomment the below)
moodle_org_token: ${{ secrets.MOODLE_ORG_TOKEN }}
with:
#Grunt fails due to CSS styling needing an !important.
disable_grunt: true
release_branches: master
-12
View File
@@ -1,12 +0,0 @@
#NOTE: This master branch has been deprecated. Please see the table below for the correct supported branches.
## Branches
| Version | Branch | Patches |
|-----------------|--------------|----------------------|
| Moodle 4.0 - 4.2| MOODLE_400_STABLE | None |
| Moodle 3.8 -3.9 | MOODLE_35_STABLE | None |
| Moodle 3.7 | MOODLE_35_STABLE | MDL-66340 |
| Moodle 3.5-3.6 | MOODLE_35_STABLE | MDL-66340, MDL-60470 |
| Totara 12-15 | MOODLE_35_STABLE | MDL-66340, MDL-60470 |
@@ -16,6 +16,8 @@
namespace tool_mfa\event;
use stdClass;
/**
* Event for when user factor is deleted.
*
@@ -37,11 +39,11 @@ class user_deleted_factor extends \core\event\base {
* @param stdClass $deleteuser the user who performed the factor delete.
* @param string $factorname deleted factor
*
* @return user_factor_deleted the user_factor_deleted event
* @return \core\event\base the user_factor_deleted event
*
* @throws \coding_exception
*/
public static function user_deleted_factor_event($user, $deleteuser, $factorname) {
public static function user_deleted_factor_event(stdClass $user, $deleteuser, $factorname): \core\event\base {
$data = [
'relateduserid' => $user->id,
@@ -61,7 +63,7 @@ class user_deleted_factor extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -71,7 +73,7 @@ class user_deleted_factor extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
// The log message changed from logging the deleter user object to the ID. This must be kept for backwards compat
// With old log events.
if (is_object($this->other['delete'])) {
@@ -89,7 +91,7 @@ class user_deleted_factor extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:userdeletedfactor', 'tool_mfa');
}
}
@@ -16,6 +16,8 @@
namespace tool_mfa\event;
use stdClass;
/**
* Event for when user successfully passed all MFA factor checks.
*
@@ -33,13 +35,13 @@ class user_failed_mfa extends \core\event\base {
/**
* Create instance of event.
*
* @param int $user the User object of the User who failed MFA authentication.
* @param stdClass $user the User object of the User who failed MFA authentication.
*
* @return user_failed_mfa the user_passed_mfa event
*
* @throws \coding_exception
*/
public static function user_failed_mfa_event($user) {
public static function user_failed_mfa_event(stdClass $user): user_failed_mfa {
// Build debug info string.
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
$debug = '';
@@ -71,7 +73,7 @@ class user_failed_mfa extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -81,7 +83,7 @@ class user_failed_mfa extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
return "The user with id '{$this->other['userid']}' failed authenticating with MFA.
<br> Information: {$this->other['failurereason']}{$this->other['debug']}";
}
@@ -92,7 +94,7 @@ class user_failed_mfa extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:userfailedmfa', 'tool_mfa');
}
}
@@ -16,6 +16,8 @@
namespace tool_mfa\event;
use stdClass;
/**
* Event for when user successfully passed all MFA factor checks.
*
@@ -33,13 +35,13 @@ class user_passed_mfa extends \core\event\base {
/**
* Create instance of event.
*
* @param int $user the User object of the User who passed all MFA factor checks.
* @param stdClass $user the User object of the User who passed all MFA factor checks.
*
* @return user_passed_mfa the user_passed_mfa event
*
* @throws \coding_exception
*/
public static function user_passed_mfa_event($user) {
public static function user_passed_mfa_event(stdClass $user): user_passed_mfa {
// Build debug info string.
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
@@ -65,7 +67,7 @@ class user_passed_mfa extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -75,7 +77,7 @@ class user_passed_mfa extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
return "The user with id '{$this->other['userid']}' successfully passed MFA. <br> Information: {$this->other['debug']}";
}
@@ -85,7 +87,7 @@ class user_passed_mfa extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:userpassedmfa', 'tool_mfa');
}
}
@@ -16,6 +16,8 @@
namespace tool_mfa\event;
use stdClass;
/**
* Event for when user successfully revoked MFA Factor.
*
@@ -33,14 +35,14 @@ class user_revoked_factor extends \core\event\base {
/**
* Create instance of event.
*
* @param int $user the User object of the User who has revoked new factor
* @param stdClass $user the User object of the User who has revoked new factor
* @param string $factorname revoked factor
*
* @return user_passed_mfa the user_passed_mfa event
* @return self the related event
*
* @throws \coding_exception
*/
public static function user_revoked_factor_event($user, $factorname) {
public static function user_revoked_factor_event(stdClass $user, $factorname): self {
$data = [
'relateduserid' => null,
@@ -59,7 +61,7 @@ class user_revoked_factor extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -69,7 +71,7 @@ class user_revoked_factor extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
return "The user with id '{$this->other['userid']}' successfully revoked {$this->other['factorname']}";
}
@@ -79,7 +81,7 @@ class user_revoked_factor extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:userrevokedfactor', 'tool_mfa');
}
}
@@ -16,6 +16,8 @@
namespace tool_mfa\event;
use stdClass;
/**
* Event for when user successfully setup new MFA Factor.
*
@@ -33,14 +35,14 @@ class user_setup_factor extends \core\event\base {
/**
* Create instance of event.
*
* @param object $user the User object of the User who has setup new factor
* @param stdClass $user the User object of the User who has setup new factor
* @param string $factorname setup factor
*
* @return user_passed_mfa the user_passed_mfa event
* @return self the related event
*
* @throws \coding_exception
*/
public static function user_setup_factor_event($user, $factorname) {
public static function user_setup_factor_event(stdClass $user, $factorname): self {
$data = [
'relateduserid' => null,
@@ -59,7 +61,7 @@ class user_setup_factor extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -69,7 +71,7 @@ class user_setup_factor extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
return "The user with id '{$this->other['userid']}' successfully setup {$this->other['factorname']}";
}
@@ -79,7 +81,7 @@ class user_setup_factor extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:usersetupfactor', 'tool_mfa');
}
}
@@ -43,9 +43,9 @@ class admin_setting_managemfa extends \admin_setting {
/**
* Always returns true
*
* @return true
* @return bool
*/
public function get_setting() {
public function get_setting(): bool {
return true;
}
@@ -55,7 +55,7 @@ class admin_setting_managemfa extends \admin_setting {
* @param mixed $data
* @return string Always returns ''
*/
public function write_setting($data) {
public function write_setting($data): string {
return '';
}
@@ -69,7 +69,7 @@ class admin_setting_managemfa extends \admin_setting {
* @throws \coding_exception
* @throws \moodle_exception
*/
public function output_html($data, $query='') {
public function output_html($data, $query=''): string {
global $OUTPUT;
$return = $OUTPUT->box_start('generalbox');
@@ -36,21 +36,21 @@ class fallback extends object_factor_base {
/**
* {@inheritDoc}
*/
public function get_display_name() {
public function get_display_name(): string {
return get_string('fallback', 'tool_mfa');
}
/**
* {@inheritDoc}
*/
public function get_info() {
public function get_info(): string {
return get_string('fallback_info', 'tool_mfa');
}
/**
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
return \tool_mfa\plugininfo\factor::STATE_FAIL;
}
@@ -58,10 +58,10 @@ class fallback extends object_factor_base {
* Sets the state of the factor check into the session.
* Returns whether storing the var was successful.
*
* @param mixed $state
* @param string $state
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return false;
}
}
@@ -25,6 +25,8 @@
namespace tool_mfa\local\factor;
use stdClass;
interface object_factor {
/**
@@ -33,7 +35,7 @@ interface object_factor {
* @return bool
* @throws \dml_exception
*/
public function is_enabled();
public function is_enabled(): bool;
/**
* Returns configured factor weight.
@@ -41,7 +43,7 @@ interface object_factor {
* @return int
* @throws \dml_exception
*/
public function get_weight();
public function get_weight(): int;
/**
* Returns factor name from language string.
@@ -49,7 +51,7 @@ interface object_factor {
* @return string
* @throws \coding_exception
*/
public function get_display_name();
public function get_display_name(): string;
/**
* Returns factor info from language string.
@@ -57,7 +59,7 @@ interface object_factor {
* @return string
* @throws \coding_exception
*/
public function get_info();
public function get_info(): string;
/**
* Defines setup_factor form definition page for particular factor.
@@ -66,7 +68,7 @@ interface object_factor {
* @return object $mform
* @throws \coding_exception
*/
public function setup_factor_form_definition($mform);
public function setup_factor_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm;
/**
* Defines setup_factor form definition page after form data has been set.
@@ -75,7 +77,7 @@ interface object_factor {
* @return object $mform
* @throws \coding_exception
*/
public function setup_factor_form_definition_after_data($mform);
public function setup_factor_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm;
/**
* Implements setup_factor form validation for particular factor.
@@ -84,7 +86,7 @@ interface object_factor {
* @param array $data
* @return array
*/
public function setup_factor_form_validation($data);
public function setup_factor_form_validation(array $data): array;
/**
* Defines login form definition page for particular factor.
@@ -93,7 +95,7 @@ interface object_factor {
* @return object $mform
* @throws \coding_exception
*/
public function login_form_definition($mform);
public function login_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm;
/**
* Defines login form definition page after form data has been set.
@@ -102,7 +104,7 @@ interface object_factor {
* @return object $mform
* @throws \coding_exception
*/
public function login_form_definition_after_data($mform);
public function login_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm;
/**
* Implements login form validation for particular factor.
@@ -111,16 +113,16 @@ interface object_factor {
* @param array $data
* @return array
*/
public function login_form_validation($data);
public function login_form_validation(array $data): array;
/**
* Setups given factor and adds it to user's active factors list.
* Returns true if factor has been successfully added, otherwise false.
*
* @param array $data
* @return stdClass the factor record, or null.
* @param stdClass $data
* @return stdClass|null the factor record, or null.
*/
public function setup_user_factor($data);
public function setup_user_factor(stdClass $data): stdClass|null;
/**
* Returns an array of all user factors of given type (both active and revoked).
@@ -128,7 +130,7 @@ interface object_factor {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user);
public function get_all_user_factors(stdClass $user): array;
/**
* Returns an array of active user factor records.
@@ -137,7 +139,7 @@ interface object_factor {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_active_user_factors($user);
public function get_active_user_factors(stdClass $user): array;
/**
* Returns true if factor class has factor records that might be revoked.
@@ -145,7 +147,7 @@ interface object_factor {
*
* @return bool
*/
public function has_revoke();
public function has_revoke(): bool;
/**
* Marks factor record as revoked.
@@ -154,97 +156,98 @@ interface object_factor {
* @param int $factorid
* @return bool
*/
public function revoke_user_factor($factorid);
public function revoke_user_factor(?int $factorid = null): bool;
/**
* When validation code is correct - update lastverified field for given factor.
* If factor id is not provided, update all factor entries for user.
*
* @param int $factorid
* @return bool
* @return bool|\dml_exception
*/
public function update_lastverified($factorid);
public function update_lastverified(?int $factorid = null): bool|\dml_exception;
/**
* Gets lastverified timestamp.
*
* @param int $factorid
* @return int
* @return int|bool
*/
public function get_lastverified($factorid);
public function get_lastverified(int $factorid): int|bool;
/**
* Returns true if factor needs to be setup by user and has setup_form.
*
* @return bool
*/
public function has_setup();
public function has_setup(): bool;
/**
* If has_setup returns true, decides if the setup buttons should be shown on the preferences page.
*
* @return bool
*/
public function show_setup_buttons();
public function show_setup_buttons(): bool;
/**
* Returns true if factor requires user input for success or failure during login.
*
* @return bool
*/
public function has_input();
public function has_input(): bool;
/**
* Returns the state of the factor check
*
* @return mixed
* @return string
*/
public function get_state();
public function get_state(): string;
/**
* Sets the state of the factor check into the session.
* Returns whether storing the var was successful.
*
* @param mixed $state
* @param string $state
* @return bool
*/
public function set_state($state);
public function set_state(string $state): bool;
/**
* Fires any additional actions required by the factor once the user reaches the pass state.
*
* @return void
*/
public function post_pass_state();
public function post_pass_state(): void;
/**
* Retrieves label for a factorid.
*
* @param int $factorid
* @return string
* @return string|\dml_exception
*/
public function get_label($factorid);
public function get_label(int $factorid): string|\dml_exception;
/**
* Returns a list of urls to not redirect from.
*
* @return array
*/
public function get_no_redirect_urls();
public function get_no_redirect_urls(): array;
/**
* Returns all possible states for a user.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user);
public function possible_states(stdClass $user): array;
/**
* Return summary condition for passing factor.
*
* @return array
* @return string
*/
public function get_summary_condition();
public function get_summary_condition(): string;
/**
* Checks whether the factor combination is valid based on factor behaviour.
@@ -254,28 +257,29 @@ interface object_factor {
* @param array $combination array of factors that make up the combination
* @return bool
*/
public function check_combination($combination);
public function check_combination(array $combination): bool;
/**
* Gets the string for setup button on preferences page.
*
* @return string the string to display on the button.
*/
public function get_setup_string();
public function get_setup_string(): string;
/**
* Deletes all instances of a factor for user.
*
* @param stdClass $user the user to delete for.
* @return void
*/
public function delete_factor_for_user($user);
public function delete_factor_for_user(stdClass $user): void;
/**
* Process a cancel action from a user.
*
* @return void
*/
public function process_cancel_action();
public function process_cancel_action(): void;
/**
* Hook point for global auth form action hooks.
@@ -283,7 +287,7 @@ interface object_factor {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function global_definition($mform);
public function global_definition(\MoodleQuickForm $mform): void;
/**
* Hook point for global auth form action hooks.
@@ -291,7 +295,7 @@ interface object_factor {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function global_definition_after_data($mform);
public function global_definition_after_data(\MoodleQuickForm $mform): void;
/**
* Hook point for global auth form action hooks.
@@ -300,12 +304,13 @@ interface object_factor {
* @param array $files Files form the form.
* @return array of errors from validation.
*/
public function global_validation($data, $files): array;
public function global_validation(array $data, array $files): array;
/**
* Hook point for global auth form action hooks.
*
* @param object $data Data from the form.
* @return void
*/
public function global_submit($data);
public function global_submit(object $data): void;
}
@@ -16,6 +16,8 @@
namespace tool_mfa\local\factor;
use stdClass;
/**
* MFA factor abstract class.
*
@@ -54,10 +56,11 @@ abstract class object_factor_base implements object_factor {
/**
* This loads the locked state from the DB
*
* Base class implementation.
*
* @return void
*/
public function load_locked_state() {
public function load_locked_state(): void {
global $DB, $USER;
// Check if lockcounter column exists (incase upgrade hasnt run yet).
@@ -92,7 +95,7 @@ abstract class object_factor_base implements object_factor {
* @return bool
* @throws \dml_exception
*/
public function is_enabled() {
public function is_enabled(): bool {
$status = get_config('factor_'.$this->name, 'enabled');
if ($status == 1) {
return true;
@@ -108,7 +111,7 @@ abstract class object_factor_base implements object_factor {
* @return int
* @throws \dml_exception
*/
public function get_weight() {
public function get_weight(): int {
$weight = get_config('factor_'.$this->name, 'weight');
if ($weight) {
return (int) $weight;
@@ -124,7 +127,7 @@ abstract class object_factor_base implements object_factor {
* @return string
* @throws \coding_exception
*/
public function get_display_name() {
public function get_display_name(): string {
return get_string('pluginname', 'factor_'.$this->name);
}
@@ -136,7 +139,7 @@ abstract class object_factor_base implements object_factor {
* @return string
* @throws \coding_exception
*/
public function get_info() {
public function get_info(): string {
return get_string('info', 'factor_'.$this->name);
}
@@ -148,7 +151,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function setup_factor_form_definition($mform) {
public function setup_factor_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
return $mform;
}
@@ -160,7 +163,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function setup_factor_form_definition_after_data($mform) {
public function setup_factor_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm {
return $mform;
}
@@ -173,7 +176,7 @@ abstract class object_factor_base implements object_factor {
* @param array $data
* @return array
*/
public function setup_factor_form_validation($data) {
public function setup_factor_form_validation(array $data): array {
return [];
}
@@ -183,10 +186,10 @@ abstract class object_factor_base implements object_factor {
*
* Dummy implementation. Should be overridden in child class.
*
* @param array $data
* @return stdClass the record if created, or null.
* @param stdClass $data
* @return stdClass|null the record if created, or null.
*/
public function setup_user_factor($data) {
public function setup_user_factor(stdClass $data): stdClass|null {
return null;
}
@@ -198,7 +201,7 @@ abstract class object_factor_base implements object_factor {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
return [];
}
@@ -209,7 +212,7 @@ abstract class object_factor_base implements object_factor {
* @param stdClass $user object to check against.
* @return array
*/
public function get_active_user_factors($user) {
public function get_active_user_factors(stdClass $user): array {
$return = [];
$factors = $this->get_all_user_factors($user);
foreach ($factors as $factor) {
@@ -228,7 +231,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function login_form_definition($mform) {
public function login_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
return $mform;
}
@@ -240,7 +243,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function login_form_definition_after_data($mform) {
public function login_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm {
return $mform;
}
@@ -253,7 +256,7 @@ abstract class object_factor_base implements object_factor {
* @param array $data
* @return array
*/
public function login_form_validation($data) {
public function login_form_validation(array $data): array {
return [];
}
@@ -265,7 +268,7 @@ abstract class object_factor_base implements object_factor {
*
* @return bool
*/
public function has_revoke() {
public function has_revoke(): bool {
return false;
}
@@ -277,7 +280,7 @@ abstract class object_factor_base implements object_factor {
* @return bool
* @throws \dml_exception
*/
public function revoke_user_factor($factorid = null) {
public function revoke_user_factor(?int $factorid = null): bool {
global $DB, $USER;
if (!empty($factorid)) {
@@ -307,10 +310,10 @@ abstract class object_factor_base implements object_factor {
* When validation code is correct - update lastverified field for given factor.
* If factor id is not provided, update all factor entries for user.
* @param int $factorid
* @return bool
* @return bool|\dml_exception
* @throws \dml_exception
*/
public function update_lastverified($factorid = null) {
public function update_lastverified(?int $factorid = null): bool|\dml_exception {
global $DB, $USER;
if (!empty($factorid)) {
$params = ['id' => $factorid];
@@ -326,7 +329,7 @@ abstract class object_factor_base implements object_factor {
* @param int $factorid
* @return int|bool the lastverified timestamp, or false if not found.
*/
public function get_lastverified($factorid) {
public function get_lastverified(int $factorid): int|bool {
global $DB;
$record = $DB->get_record('tool_mfa', ['id' => $factorid]);
@@ -335,12 +338,11 @@ abstract class object_factor_base implements object_factor {
/**
* Returns true if factor needs to be setup by user and has setup_form.
*
* Override in child class if necessary.
*
* @return bool
*/
public function has_setup() {
public function has_setup(): bool {
return false;
}
@@ -349,7 +351,7 @@ abstract class object_factor_base implements object_factor {
*
* @return bool
*/
public function show_setup_buttons() {
public function show_setup_buttons(): bool {
return $this->has_setup();
}
@@ -360,7 +362,7 @@ abstract class object_factor_base implements object_factor {
*
* @return bool
*/
public function has_input() {
public function has_input(): bool {
return true;
}
@@ -372,7 +374,7 @@ abstract class object_factor_base implements object_factor {
*
* @return bool
*/
public function is_lockable() {
public function is_lockable(): bool {
return $this->has_input();
}
@@ -384,7 +386,7 @@ abstract class object_factor_base implements object_factor {
*
* @return mixed
*/
public function get_state() {
public function get_state(): string {
global $SESSION;
$property = 'factor_'.$this->name;
@@ -405,7 +407,7 @@ abstract class object_factor_base implements object_factor {
* @param mixed $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
global $SESSION;
// Do not allow overwriting fail states.
@@ -424,7 +426,7 @@ abstract class object_factor_base implements object_factor {
* @param object $user
* @return void
*/
public function create_event_after_factor_setup($user) {
public function create_event_after_factor_setup(object $user): void {
$event = \tool_mfa\event\user_setup_factor::user_setup_factor_event($user, $this->get_display_name());
$event->trigger();
}
@@ -432,8 +434,10 @@ abstract class object_factor_base implements object_factor {
/**
* Function for factor actions in the pass state.
* Override in child class if necessary.
*
* @return void
*/
public function post_pass_state() {
public function post_pass_state(): void {
// Update lastverified for factor.
if ($this->get_state() == \tool_mfa\plugininfo\factor::STATE_PASS) {
$this->update_lastverified();
@@ -447,16 +451,19 @@ abstract class object_factor_base implements object_factor {
* Function to retrieve the label for a factorid.
*
* @param int $factorid
* @return string|\dml_exception
*/
public function get_label($factorid) {
public function get_label(int $factorid): string|\dml_exception {
global $DB;
return $DB->get_field('tool_mfa', 'label', ['id' => $factorid]);
}
/**
* Function to get urls that should not be redirected from.
*
* @return array
*/
public function get_no_redirect_urls() {
public function get_no_redirect_urls(): array {
return [];
}
@@ -466,9 +473,10 @@ abstract class object_factor_base implements object_factor {
* This should be overridden in factors where state is non-deterministic.
* E.g. IP changes based on whether a user is using a VPN.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [$this->get_state()];
}
@@ -476,8 +484,10 @@ abstract class object_factor_base implements object_factor {
* Returns condition for passing factor.
* Implementation for basic conditions.
* Override for complex conditions such as auth type.
*
* @return string
*/
public function get_summary_condition() {
public function get_summary_condition(): string {
return get_string('summarycondition', 'factor_'.$this->name);
}
@@ -489,14 +499,16 @@ abstract class object_factor_base implements object_factor {
* @param array $combination array of factors that make up the combination
* @return bool
*/
public function check_combination($combination) {
public function check_combination(array $combination): bool {
return true;
}
/**
* Gets the string for setup button on preferences page.
*
* @return string
*/
public function get_setup_string() {
public function get_setup_string(): string {
return get_string('setupfactor', 'tool_mfa');
}
@@ -504,8 +516,9 @@ abstract class object_factor_base implements object_factor {
* Deletes all instances of factor for a user.
*
* @param stdClass $user the user to delete for.
* @return void
*/
public function delete_factor_for_user($user) {
public function delete_factor_for_user(stdClass $user): void {
global $DB, $USER;
$DB->delete_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -519,7 +532,7 @@ abstract class object_factor_base implements object_factor {
*
* @return void
*/
public function increment_lock_counter() {
public function increment_lock_counter(): void {
global $DB, $USER;
// First make sure the state is loaded.
@@ -549,7 +562,7 @@ abstract class object_factor_base implements object_factor {
*
* @return int the number of attempts at this factor remaining.
*/
public function get_remaining_attempts() {
public function get_remaining_attempts(): int {
$lockthreshold = get_config('tool_mfa', 'lockout');
if ($this->lockcounter === -1) {
// If upgrade.php hasnt been run yet, just return 10.
@@ -564,7 +577,7 @@ abstract class object_factor_base implements object_factor {
*
* @return void
*/
public function process_cancel_action() {
public function process_cancel_action(): void {
$this->set_state(\tool_mfa\plugininfo\factor::STATE_NEUTRAL);
}
@@ -574,7 +587,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function global_definition($mform) {
public function global_definition(\MoodleQuickForm $mform): void {
return;
}
@@ -584,7 +597,7 @@ abstract class object_factor_base implements object_factor {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function global_definition_after_data($mform) {
public function global_definition_after_data(\MoodleQuickForm $mform): void {
return;
}
@@ -595,7 +608,7 @@ abstract class object_factor_base implements object_factor {
* @param array $files Files form the form.
* @return array of errors from validation.
*/
public function global_validation($data, $files): array {
public function global_validation(array $data, array $files): array {
return [];
}
@@ -603,8 +616,9 @@ abstract class object_factor_base implements object_factor {
* Hook point for global auth form action hooks.
*
* @param object $data Data from the form.
* @return void
*/
public function global_submit($data) {
public function global_submit(object $data): void {
return;
}
}
@@ -43,7 +43,7 @@ class global_form_manager {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function definition(&$mform) {
public function definition(\MoodleQuickForm &$mform): void {
foreach ($this->activefactors as $factor) {
$factor->global_definition($mform);
}
@@ -55,7 +55,7 @@ class global_form_manager {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function definition_after_data(&$mform) {
public function definition_after_data(\MoodleQuickForm &$mform): void {
foreach ($this->activefactors as $factor) {
$factor->global_definition_after_data($mform);
}
@@ -68,7 +68,7 @@ class global_form_manager {
* @param array $files Files form the form.
* @return array of errors from validation.
*/
public function validation($data, $files) {
public function validation(array $data, array $files): array {
$errors = [];
foreach ($this->activefactors as $factor) {
$errors = array_merge($errors, $factor->global_validation($data, $files));
@@ -82,7 +82,7 @@ class global_form_manager {
* @param \stdClass $data Data from the form.
* @return void
*/
public function submit(\stdClass $data) {
public function submit(\stdClass $data): void {
foreach ($this->activefactors as $factor) {
$factor->global_submit($data);
}
@@ -65,7 +65,7 @@ class login_form extends \moodleform {
* {@inheritDoc}
* @see moodleform::definition()
*/
public function definition() {
public function definition(): void {
$mform = $this->_form;
$factor = $this->_customdata['factor'];
$mform = $factor->login_form_definition($mform);
@@ -74,8 +74,10 @@ class login_form extends \moodleform {
/**
* Invokes factor login_form_definition_after_data() method after form data has been set.
*
* @return void
*/
public function definition_after_data() {
public function definition_after_data(): void {
$mform = $this->_form;
$factor = $this->_customdata['factor'];
@@ -32,7 +32,7 @@ class reset_factor extends \moodleform {
/**
* Form definition.
*/
public function definition() {
public function definition(): void {
$mform = $this->_form;
$factors = $this->_customdata['factors'];
$bulkaction = $this->_customdata['bulk'];
@@ -34,7 +34,7 @@ class revoke_factor_form extends \moodleform {
* {@inheritDoc}
* @see moodleform::definition()
*/
public function definition() {
public function definition(): void {
global $OUTPUT;
$mform = $this->_form;
$factorname = $this->_customdata['factorname'];
@@ -34,7 +34,7 @@ class setup_factor_form extends \moodleform {
* {@inheritDoc}
* @see moodleform::definition()
*/
public function definition() {
public function definition(): void {
$mform = $this->_form;
$factorname = $this->_customdata['factorname'];
@@ -64,7 +64,7 @@ class setup_factor_form extends \moodleform {
/**
* Invokes factor setup_factor_form_definition_after_data() method after form data has been set.
*/
public function definition_after_data() {
public function definition_after_data(): void {
$mform = $this->_form;
$factorname = $this->_customdata['factorname'];
@@ -75,13 +75,12 @@ class setup_factor_form extends \moodleform {
}
/**
* In newer versions of Totara with consistent cleaning enabled we need to ensure to mark static elements
* as "xss safe". Or in Totara's ideal world to use 'html' if form-like display is not required.
* Form elements clean up
*
* @param \HTML_QuickForm $mform
* @return void
*/
private function xss_whitelist_static_form_elements($mform) {
private function xss_whitelist_static_form_elements($mform): void {
if (!method_exists('MoodleQuickForm_static', 'set_allow_xss')) {
return;
}
@@ -82,7 +82,7 @@ class verification_field extends \MoodleQuickForm_text {
*
* @return string
*/
public function toHtml() {
public function toHtml(): string {
// Empty the value after all attributes decided.
$this->_attributes['value'] = '';
$result = parent::toHtml();
@@ -88,7 +88,7 @@ class secret_manager {
* @param string $sessionid an optional sessionID to tie this record to
* @return void
*/
private function add_secret_to_db(string $secret, int $expires, string $sessionid = null) {
private function add_secret_to_db(string $secret, int $expires, string $sessionid = null): void {
global $DB, $USER;
$expirytime = time() + $expires;
@@ -178,7 +178,7 @@ class secret_manager {
* @param int $userid the userid to revoke the secret for.
* @return void
*/
public function revoke_secret(string $secret, $userid = null) {
public function revoke_secret(string $secret, $userid = null): void {
global $DB, $USER;
$userid = $userid ?? $USER->id;
@@ -229,7 +229,7 @@ class secret_manager {
* @param int $userid the userid to cleanup temp secrets for.
* @return void
*/
public function cleanup_temp_secrets($userid = null) {
public function cleanup_temp_secrets($userid = null): void {
global $DB, $USER;
// Session records are autocleaned up.
// Only DB cleanup required.
+38 -32
View File
@@ -16,6 +16,8 @@
namespace tool_mfa;
use dml_exception;
/**
* MFA management class.
*
@@ -40,8 +42,10 @@ class manager {
/**
* Displays a debug table with current factor information.
*
* @return void
*/
public static function display_debug_notification() {
public static function display_debug_notification(): void {
global $OUTPUT, $PAGE;
if (!get_config('tool_mfa', 'debugmode')) {
@@ -142,7 +146,7 @@ class manager {
*
* @return int
*/
public static function get_total_weight() {
public static function get_total_weight(): int {
$totalweight = 0;
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
@@ -162,7 +166,7 @@ class manager {
* @return bool
* @throws \dml_exception
*/
public static function is_factorid_valid($factorid, $user) {
public static function is_factorid_valid(int $factorid, object $user): bool {
global $DB;
return $DB->record_exists('tool_mfa', ['userid' => $user->id, 'id' => $factorid]);
}
@@ -172,7 +176,7 @@ class manager {
*
* @return void
*/
public static function cannot_login() {
public static function cannot_login(): void {
global $ME, $PAGE, $SESSION, $USER;
// Determine page URL without triggering warnings from $PAGE.
@@ -205,7 +209,7 @@ class manager {
*
* @return void
*/
public static function mfa_logout() {
public static function mfa_logout(): void {
$authsequence = get_enabled_auth_plugins();
foreach ($authsequence as $authname) {
$authplugin = get_auth_plugin($authname);
@@ -217,9 +221,9 @@ class manager {
/**
* Function to get the overall status of a user's authentication.
*
* @return mixed a STATE variable from plugininfo
* @return string a STATE variable from plugininfo
*/
public static function get_status() {
public static function get_status(): string {
global $SESSION;
// Check for any instant fail states.
@@ -264,7 +268,7 @@ class manager {
* @param bool $shouldreload whether the function should reload (used for auth.php).
* @return void
*/
public static function resolve_mfa_status($shouldreload = false) {
public static function resolve_mfa_status(bool $shouldreload = false): void {
global $SESSION;
$state = self::get_status();
@@ -298,7 +302,7 @@ class manager {
*
* @return bool true if user has passed enough factors.
*/
public static function passed_enough_factors() {
public static function passed_enough_factors(): bool {
// Check for any instant fail states.
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
@@ -321,7 +325,7 @@ class manager {
*
* @return void
*/
public static function set_pass_state() {
public static function set_pass_state(): void {
global $DB, $SESSION, $USER;
if (!isset($SESSION->tool_mfa_authenticated)) {
$SESSION->tool_mfa_authenticated = true;
@@ -390,7 +394,7 @@ class manager {
*
* @return void
*/
private static function update_pass_time() {
private static function update_pass_time(): void {
global $DB, $USER;
$exists = $DB->record_exists('tool_mfa_auth', ['userid' => $USER->id]);
@@ -405,11 +409,11 @@ class manager {
/**
* Checks whether the user should be redirected from the provided url.
*
* @param \moodle_url $url
* @param bool $preventredirect
* @param string|\moodle_url $url
* @param bool|null $preventredirect
* @return int
*/
public static function should_require_mfa($url, $preventredirect) {
public static function should_require_mfa(string|\moodle_url $url, bool|null $preventredirect): int {
global $CFG, $USER, $SESSION;
// If no cookies then no session so cannot do MFA.
@@ -486,13 +490,10 @@ class manager {
// Site policy.
if (isset($USER->policyagreed) && !$USER->policyagreed) {
// Privacy classes may not exist in older Moodles/Totara.
if (class_exists('\core_privacy\local\sitepolicy\manager')) {
$manager = new \core_privacy\local\sitepolicy\manager();
$policyurl = $manager->get_redirect_url(false);
if (!empty($policyurl) && $url->compare($policyurl, URL_MATCH_BASE)) {
return self::NO_REDIRECT;
}
$manager = new \core_privacy\local\sitepolicy\manager();
$policyurl = $manager->get_redirect_url(false);
if (!empty($policyurl) && $url->compare($policyurl, URL_MATCH_BASE)) {
return self::NO_REDIRECT;
}
}
@@ -547,8 +548,9 @@ class manager {
/**
* Clears the redirect counter for infinite redirect loops. Called from auth.php when a valid load is resolved.
*
* @return void
*/
public static function clear_redirect_counter() {
public static function clear_redirect_counter(): void {
global $SESSION;
unset($SESSION->mfa_redir_referer);
@@ -560,7 +562,7 @@ class manager {
*
* @return array
*/
public static function get_no_redirect_urls() {
public static function get_no_redirect_urls(): array {
$factors = \tool_mfa\plugininfo\factor::get_factors();
$urls = [
new \moodle_url('/login/logout.php'),
@@ -582,8 +584,10 @@ class manager {
/**
* Sleeps for an increasing period of time.
*
* @return void
*/
public static function sleep_timer() {
public static function sleep_timer(): void {
global $USER;
$duration = get_user_preferences('mfa_sleep_duration', null, $USER);
@@ -596,7 +600,7 @@ class manager {
$duration = 0.05;
}
set_user_preference('mfa_sleep_duration', $duration, $USER);
sleep($duration);
sleep((int)$duration);
}
/**
@@ -611,7 +615,7 @@ class manager {
* @return void
*/
public static function require_auth($courseorid = null, $autologinguest = null, $cm = null,
$setwantsurltome = null, $preventredirect = null) {
$setwantsurltome = null, $preventredirect = null): void {
global $PAGE, $SESSION, $FULLME;
// Guest user should never interact with MFA,
@@ -678,7 +682,7 @@ class manager {
* @return bool true or exception
* @throws dml_exception
*/
public static function set_factor_config($data, $factor) {
public static function set_factor_config(array $data, string $factor): bool|dml_exception {
$factorconf = get_config($factor);
foreach ($data as $key => $newvalue) {
if (empty($factorconf->$key)) {
@@ -701,7 +705,7 @@ class manager {
* @return bool
* @throws \dml_exception
*/
public static function is_ready() {
public static function is_ready(): bool {
global $CFG, $USER;
if (!empty($CFG->upgraderunning)) {
@@ -737,7 +741,7 @@ class manager {
* @return void
* @throws dml_exception
*/
public static function do_factor_action($factorname, $action) {
public static function do_factor_action(string $factorname, string $action): void {
$order = explode(',', get_config('tool_mfa', 'factor_order'));
$key = array_search($factorname, $order);
@@ -783,7 +787,7 @@ class manager {
*
* @return bool
*/
public static function possible_factor_setup() {
public static function possible_factor_setup(): bool {
global $USER;
// Get all active factors.
@@ -815,8 +819,10 @@ class manager {
/**
* Gets current user weight, up until first unknown factor.
*
* @return int
*/
public static function get_cumulative_weight() {
public static function get_cumulative_weight(): int {
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
$totalweight = 0;
foreach ($factors as $factor) {
@@ -839,7 +845,7 @@ class manager {
* @param string $factorname the name of the factor.
* @return bool true if factor is pending.
*/
public static function check_factor_pending($factorname) {
public static function check_factor_pending(string $factorname): bool {
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
// Setup vars.
$pending = [];
+15 -13
View File
@@ -16,6 +16,8 @@
namespace tool_mfa\plugininfo;
use stdClass;
/**
* Subplugin info class.
*
@@ -46,7 +48,7 @@ class factor extends \core\plugininfo\base {
*
* @return array of factor objects.
*/
public static function get_factors() {
public static function get_factors(): array {
$return = [];
$factors = \core_plugin_manager::instance()->get_plugins_of_type('factor');
@@ -66,7 +68,7 @@ class factor extends \core\plugininfo\base {
* @return array of factor objects
* @throws \dml_exception
*/
public static function sort_factors_by_order($unsorted) {
public static function sort_factors_by_order(array $unsorted): array {
$sorted = [];
$orderarray = explode(',', get_config('tool_mfa', 'factor_order'));
@@ -90,7 +92,7 @@ class factor extends \core\plugininfo\base {
*
* @return mixed factor object or false if factor not found.
*/
public static function get_factor($name) {
public static function get_factor(string $name): object|bool {
$factors = \core_plugin_manager::instance()->get_plugins_of_type('factor');
foreach ($factors as $factor) {
@@ -110,7 +112,7 @@ class factor extends \core\plugininfo\base {
*
* @return array of factor objects
*/
public static function get_enabled_factors() {
public static function get_enabled_factors(): array {
$return = [];
$factors = self::get_factors();
@@ -128,7 +130,7 @@ class factor extends \core\plugininfo\base {
*
* @return array of factor objects.
*/
public static function get_active_user_factor_types() {
public static function get_active_user_factor_types(): array {
global $USER;
$return = [];
$factors = self::get_enabled_factors();
@@ -149,7 +151,7 @@ class factor extends \core\plugininfo\base {
* @param stdClass $user the user to get types for.
* @return array of factor objects.
*/
public static function get_active_other_user_factor_types($user) {
public static function get_active_other_user_factor_types(stdClass $user): array {
$return = [];
$factors = self::get_enabled_factors();
@@ -168,7 +170,7 @@ class factor extends \core\plugininfo\base {
*
* @return mixed factor object the next factor to be authenticated or false.
*/
public static function get_next_user_factor() {
public static function get_next_user_factor(): object {
$factors = self::get_active_user_factor_types();
foreach ($factors as $factor) {
@@ -189,7 +191,7 @@ class factor extends \core\plugininfo\base {
*
* @return array
*/
public static function get_factor_actions() {
public static function get_factor_actions(): array {
$actions = [];
$actions[] = 'setup';
$actions[] = 'revoke';
@@ -212,7 +214,7 @@ class factor extends \core\plugininfo\base {
*
* @return null|bool
*/
public function is_enabled() {
public function is_enabled(): null|bool {
if (!$this->rootdir) {
// Plugin missing.
return false;
@@ -232,7 +234,7 @@ class factor extends \core\plugininfo\base {
*
* @return string
*/
public function get_settings_section_name() {
public function get_settings_section_name(): string {
return $this->type . '_' . $this->name;
}
@@ -246,7 +248,7 @@ class factor extends \core\plugininfo\base {
* @param string $parentnodename
* @param bool $hassiteconfig whether the current user has moodle/site:config capability
*/
public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig): void {
if (!$this->is_installed_and_upgraded()) {
return;
@@ -274,7 +276,7 @@ class factor extends \core\plugininfo\base {
*
* @return bool
*/
public static function factor_exists($factorname) {
public static function factor_exists(string $factorname): bool {
$factor = self::get_factor($factorname);
return !$factor ? false : true;
}
@@ -286,7 +288,7 @@ class factor extends \core\plugininfo\base {
*
* @return stdClass|null Factor instance or nothing if not found.
*/
public static function get_instance_from_id($factorid) {
public static function get_instance_from_id(int $factorid): stdClass|null {
global $DB;
return $DB->get_record('tool_mfa', ['id' => $factorid]);
}
+10 -5
View File
@@ -103,8 +103,9 @@ class provider implements
* Gets the list of users who have data with a context. Secrets context is a subset of this table.
*
* @param userlist $userlist the userlist containing users who have data in this context.
* @return void
*/
public static function get_users_in_context(userlist $userlist) {
public static function get_users_in_context(userlist $userlist): void {
$context = $userlist->get_context();
// If current context is system, all users are contained within, get all users.
if ($context->contextlevel == CONTEXT_SYSTEM) {
@@ -119,8 +120,9 @@ class provider implements
* Exports all data stored in provided contexts for user. Secrets should not be exported as they are transient.
*
* @param approved_contextlist $contextlist the list of contexts to export for.
* @return void
*/
public static function export_user_data(approved_contextlist $contextlist) {
public static function export_user_data(approved_contextlist $contextlist): void {
global $DB;
$userid = $contextlist->get_user()->id;
foreach ($contextlist as $context) {
@@ -166,8 +168,9 @@ class provider implements
* Deletes data for all users in context.
*
* @param context $context The context to delete for.
* @return void
*/
public static function delete_data_for_all_users_in_context(\context $context) {
public static function delete_data_for_all_users_in_context(\context $context): void {
global $DB;
// All data contained in system context.
if ($context->contextlevel == CONTEXT_SYSTEM) {
@@ -181,8 +184,9 @@ class provider implements
* Deletes all data in all provided contexts for user.
*
* @param approved_contextlist $contextlist the list of contexts to delete for.
* @return void
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
public static function delete_data_for_user(approved_contextlist $contextlist): void {
global $DB;
$userid = $contextlist->get_user()->id;
foreach ($contextlist as $context) {
@@ -199,8 +203,9 @@ class provider implements
* Given a userlist, deletes all data in all provided contexts for the users
*
* @param approved_userlist $userlist the list of users to delete data for
* @return void
*/
public static function delete_data_for_users(approved_userlist $userlist) {
public static function delete_data_for_users(approved_userlist $userlist): void {
$users = $userlist->get_users();
foreach ($users as $user) {
// Create contextlist.
-136
View File
@@ -1,136 +0,0 @@
<?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/>.
/**
* MFA upgrade library.
*
* @package tool_mfa
* @copyright 2020 Peter Burnett <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Function to upgrade tool_mfa.
*
* @param int $oldversion the version we are upgrading from
* @return bool result
*/
function xmldb_tool_mfa_upgrade($oldversion) {
global $DB;
$dbman = $DB->get_manager();
if ($oldversion < 2020050700) {
// Define field lockcounter to be added to tool_mfa.
$table = new xmldb_table('tool_mfa');
$field = new xmldb_field('lockcounter', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0', 'revoked');
// Conditionally launch add field lockcounter.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// MFA savepoint reached.
upgrade_plugin_savepoint(true, 2020050700, 'tool', 'mfa');
}
if ($oldversion < 2020051900) {
// Define index userid (not unique) to be added to tool_mfa.
$table = new xmldb_table('tool_mfa');
$index = new xmldb_index('userid', XMLDB_INDEX_NOTUNIQUE, ['userid']);
// Conditionally launch add index userid.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Define index factor (not unique) to be added to tool_mfa.
$table = new xmldb_table('tool_mfa');
$index = new xmldb_index('factor', XMLDB_INDEX_NOTUNIQUE, ['factor']);
// Conditionally launch add index factor.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Define index lockcounter (not unique) to be added to tool_mfa.
$table = new xmldb_table('tool_mfa');
$index = new xmldb_index('lockcounter', XMLDB_INDEX_NOTUNIQUE, ['userid', 'factor', 'lockcounter']);
// Conditionally launch add index lockcounter.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Mfa savepoint reached.
upgrade_plugin_savepoint(true, 2020051900, 'tool', 'mfa');
}
if ($oldversion < 2020090300) {
// Define table tool_mfa_secrets to be created.
$table = new xmldb_table('tool_mfa_secrets');
// Adding fields to table tool_mfa_secrets.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('factor', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
$table->add_field('secret', XMLDB_TYPE_CHAR, '1333', null, XMLDB_NOTNULL, null, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '15', null, XMLDB_NOTNULL, null, null);
$table->add_field('expiry', XMLDB_TYPE_INTEGER, '15', null, XMLDB_NOTNULL, null, null);
$table->add_field('revoked', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
$table->add_field('sessionid', XMLDB_TYPE_CHAR, '100', null, null, null, null);
// Adding keys to table tool_mfa_secrets.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
// Adding indexes to table tool_mfa_secrets.
$table->add_index('factor', XMLDB_INDEX_NOTUNIQUE, ['factor']);
$table->add_index('expiry', XMLDB_INDEX_NOTUNIQUE, ['expiry']);
// Conditionally launch create table for tool_mfa_secrets.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Mfa savepoint reached.
upgrade_plugin_savepoint(true, 2020090300, 'tool', 'mfa');
}
if ($oldversion < 2021021900) {
// Define table tool_mfa_auth to be created.
$table = new xmldb_table('tool_mfa_auth');
// Adding fields to table tool_mfa_auth.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('lastverified', XMLDB_TYPE_INTEGER, '15', null, XMLDB_NOTNULL, null, '0');
// Adding keys to table tool_mfa_auth.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']);
// Conditionally launch create table for tool_mfa_auth.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Mfa savepoint reached.
upgrade_plugin_savepoint(true, 2021021900, 'tool', 'mfa');
}
return true;
}
View File
View File
@@ -16,6 +16,7 @@
namespace factor_admin;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
if (is_siteadmin()) {
return \tool_mfa\plugininfo\factor::STATE_NEUTRAL;
}
@@ -83,10 +84,10 @@ class factor extends object_factor_base {
* Admin Factor implementation.
* The state can never be set. Always return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state($state): bool {
return true;
}
}
@@ -17,7 +17,6 @@
namespace factor_admin\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_admin';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -16,6 +16,7 @@
namespace factor_auth;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$safetypes = get_config('factor_auth', 'goodauth');
@@ -93,10 +94,10 @@ class factor extends object_factor_base {
* Auth Factor implementation.
* The state can never be set. Always return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -106,7 +107,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_summary_condition() {
public function get_summary_condition(): string {
$safetypes = get_config('factor_auth', 'goodauth');
return get_string('summarycondition', 'factor_'.$this->name, $safetypes);
@@ -17,7 +17,6 @@
namespace factor_auth\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2021020500; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_auth';
$plugin->release = 2021020500;
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -16,6 +16,7 @@
namespace factor_capability;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$adminpass = (bool) get_config('factor_capability', 'adminpasses');
@@ -88,10 +89,10 @@ class factor extends object_factor_base {
* User Capability implementation.
* Cannot set state, return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -99,9 +100,10 @@ class factor extends object_factor_base {
* User capability implementation.
* Possible states are either neutral or pass.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -17,7 +17,6 @@
namespace factor_capability\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2020071400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_capability';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+31 -17
View File
@@ -19,6 +19,7 @@ namespace factor_cohort;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../../../../../cohort/lib.php');
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -38,7 +39,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
if (!empty($records)) {
@@ -64,7 +65,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -74,7 +75,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$cohortstring = get_config('factor_cohort', 'cohorts');
// Nothing selected, everyone passes.
@@ -97,10 +98,10 @@ class factor extends object_factor_base {
* cohort implementation.
* Cannot set state, return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -108,9 +109,9 @@ class factor extends object_factor_base {
* cohort implementation.
* User can not influence. Result is whatever current state is.
*
* @param \stdClass $user
* @param stdClass $user
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [$this->get_state()];
}
@@ -120,20 +121,33 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_summary_condition() {
global $DB;
public function get_summary_condition(): string {
$selectedcohorts = get_config('factor_cohort', 'cohorts');
if (empty($selectedcohorts)) {
return get_string('summarycondition', 'factor_cohort', get_string('none'));
} else {
$selectedcohorts = explode(',', $selectedcohorts);
}
$names = [];
foreach ($selectedcohorts as $cohort) {
$record = $DB->get_record('cohort', ['id' => $cohort]);
$names[] = $record->name;
$selectedcohorts = $this->get_cohorts(explode(',', $selectedcohorts));
if (empty($selectedcohorts)) {
return get_string('summarycondition', 'factor_cohort', get_string('none'));
}
$string = implode(', ', $names);
return get_string('summarycondition', 'factor_cohort', $string);
return get_string('summarycondition', 'factor_cohort', implode(', ', $selectedcohorts));
}
/**
* Get array of the selected cohorts.
*
* @param array $selectedcohorts
* @return array
*/
public function get_cohorts(array $selectedcohorts) : array {
global $DB;
[$insql, $inparams] = $DB->get_in_or_equal($selectedcohorts);
$sql = "SELECT id, name FROM {cohort} WHERE id $insql";
$cohorts = $DB->get_records_sql_menu($sql, $inparams);
return $cohorts;
}
}
@@ -17,7 +17,6 @@
namespace factor_cohort\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,55 @@
<?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/>.
namespace factor_cohort;
/**
* Tests for cohort factor.
*
* @covers \factor_cohort\factor
* @package factor_cohort
* @copyright 2023 Stevani Andolo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class factor_test extends \advanced_testcase {
/**
* Tests getting the summary condition
*
* @covers ::get_summary_condition
* @covers ::get_cohorts
*/
public function test_get_summary_condition() {
$this->resetAfterTest();
set_config('enabled', 1, 'factor_cohort');
$cohortfactor = \tool_mfa\plugininfo\factor::get_factor('cohort');
$cohort = $this->getDataGenerator()->create_cohort();
$userassignover = $this->getDataGenerator()->create_user();
cohort_add_member($cohort->id, $userassignover->id);
// Add the created cohortid into factor_cohort plugin.
set_config('cohorts', $cohort->id, 'factor_cohort');
$selectedcohorts = get_config('factor_cohort', 'cohorts');
$selectedcohorts = $cohortfactor->get_cohorts(explode(',', $selectedcohorts));
$this->assertStringContainsString(
implode(', ', $selectedcohorts),
$cohortfactor->get_summary_condition()
);
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022101100; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_cohort';
$plugin->release = 'v0.2';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2022090600];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -16,6 +16,8 @@
namespace factor_email\event;
use stdClass;
/**
* Event for when a user receives an unauthorised email from MFA.
*
@@ -37,11 +39,11 @@ class unauth_email extends \core\event\base {
* @param string $ip the ip address the unauthorised email came from.
* @param string $useragent the browser fingerpring the unauthorised email came from.
*
* @return user_passed_mfa the user_passed_mfa event
* @return \core\event\base the user_passed_mfa event
*
* @throws \coding_exception
*/
public static function unauth_email_event($user, $ip, $useragent) {
public static function unauth_email_event(stdClass $user, string $ip, string $useragent): \core\event\base {
$data = [
'relateduserid' => null,
@@ -61,7 +63,7 @@ class unauth_email extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -71,9 +73,12 @@ class unauth_email extends \core\event\base {
*
* @return string
*/
public function get_description() {
return "The user with id '{$this->other['userid']}' made an unauthorised login attempt using email verification from
IP '{$this->other['ip']}' with browser agent '{$this->other['useragent']}'.";
public function get_description(): string {
$data = new stdClass();
$data->userid = $this->other['userid'];
$data->ip = $this->other['ip'];
$data->useragent = $this->other['useragent'];
return get_string('unauthloginattempt', 'factor_email', $data);
}
/**
@@ -82,7 +87,7 @@ class unauth_email extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:unauthemail', 'factor_email');
}
}
+16 -14
View File
@@ -16,6 +16,7 @@
namespace factor_email;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function login_form_definition($mform) {
public function login_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
$mform->addElement('text', 'verificationcode', get_string('verificationcode', 'factor_email'));
$mform->setType('verificationcode', PARAM_ALPHANUM);
@@ -48,7 +49,7 @@ class factor extends object_factor_base {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return object $mform
*/
public function login_form_definition_after_data($mform) {
public function login_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm {
$this->generate_and_email_code();
return $mform;
}
@@ -57,8 +58,9 @@ class factor extends object_factor_base {
* Sends and e-mail to user with given verification code.
*
* @param int $instanceid
* @return void
*/
public static function email_verification_code($instanceid) {
public static function email_verification_code(int $instanceid): void {
global $PAGE, $USER;
$noreplyuser = \core_user::get_noreply_user();
$subject = get_string('email:subject', 'factor_email');
@@ -73,7 +75,7 @@ class factor extends object_factor_base {
* @param array $data
* @return array
*/
public function login_form_validation($data) {
public function login_form_validation(array $data): array {
global $USER;
$return = [];
@@ -90,7 +92,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', [
@@ -121,7 +123,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
if (self::is_ready()) {
return true;
}
@@ -133,7 +135,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
if (!self::is_ready()) {
return \tool_mfa\plugininfo\factor::STATE_NEUTRAL;
}
@@ -146,7 +148,7 @@ class factor extends object_factor_base {
*
* @return bool
*/
private static function is_ready() {
private static function is_ready(): bool {
global $DB, $USER;
if (empty($USER->email)) {
@@ -171,7 +173,7 @@ class factor extends object_factor_base {
*
* @return void
*/
private function generate_and_email_code() {
private function generate_and_email_code(): void {
global $DB, $USER;
// Get instance that isnt parent email type (label check).
@@ -224,7 +226,7 @@ class factor extends object_factor_base {
* @param string $enteredcode
* @return bool
*/
private function check_verification_code($enteredcode) {
private function check_verification_code(string $enteredcode): bool {
global $DB, $USER;
$duration = get_config('factor_email', 'duration');
@@ -251,7 +253,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function post_pass_state() {
public function post_pass_state(): void {
global $DB, $USER;
// Delete all email records except base record.
$selectsql = 'userid = ?
@@ -269,7 +271,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_no_redirect_urls() {
public function get_no_redirect_urls(): array {
$email = new \moodle_url('/admin/tool/mfa/factor/email/email.php');
return [$email];
}
@@ -277,9 +279,9 @@ class factor extends object_factor_base {
/**
* Email factor implementation.
*
* @param \stdClass $user
* @param stdClass $user
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
// Email can return all states.
return [
\tool_mfa\plugininfo\factor::STATE_FAIL,
@@ -33,7 +33,7 @@ class email extends \moodleform {
/**
* Form definition.
*/
public function definition() {
public function definition(): void {
$mform = $this->_form;
$mform->addElement('html', get_string('email:accident', 'factor_email'));
$this->add_action_buttons(true, get_string('continue'));
@@ -49,7 +49,7 @@ class email extends \moodleform {
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files) {
public function validation($data, $files): array {
$errors = parent::validation($data, $files);
return $errors;
}
@@ -17,7 +17,6 @@
namespace factor_email\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+1 -1
View File
@@ -88,7 +88,7 @@ if ($form->is_cancelled()) {
// Suspend user account.
if (get_config('factor_email', 'suspend')) {
$DB->set_field('user', 'suspended', 1, ['id' => $userid]);
$DB->set_field('user', 'suspended', 1, ['id' => $user->id]);
}
$message = get_string('email:revokesuccess', 'factor_email', fullname($user));
@@ -52,6 +52,8 @@ $string['settings:suspend'] = 'Suspend unauthorised accounts';
$string['settings:suspend_help'] = 'Check this to suspend user accounts if an unauthorised email verification is received.';
$string['setupfactor'] = 'E-Mail Factor setup';
$string['summarycondition'] = 'has valid email setup';
$string['unauthloginattempt'] = 'The user with id {$a->userid} made an unauthorised login attempt using email verification from
IP {$a->ip} with browser agent {$a->useragent}';
$string['unauthemail'] = 'Unauthorised Email';
$string['verificationcode'] = 'Enter verification code for confirmation';
$string['verificationcode_help'] = 'Verification code has been sent to your email address';
+1 -1
View File
@@ -33,7 +33,7 @@ class factor_email_renderer extends plugin_renderer_base {
* @param int $instanceid
* @return string|boolean
*/
public function generate_email($instanceid) {
public function generate_email(int $instanceid): string|bool {
global $DB;
$instance = $DB->get_record('tool_mfa', ['id' => $instanceid]);
$authurl = new \moodle_url('/admin/tool/mfa/factor/email/email.php',
@@ -0,0 +1,86 @@
<?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/>.
namespace factor_email;
/**
* Tests for email factor.
*
* @covers \factor_email\factor
* @package factor_email
* @copyright 2023 Stevani Andolo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class factor_test extends \advanced_testcase {
/**
* Tests checking verification code
*
* @covers ::check_verification_code
* @covers ::post_pass_state
*/
public function test_check_verification_code() {
global $DB, $USER;
$this->resetAfterTest(true);
$emailfactorclass = new \factor_email\factor('email');
$rc = new \ReflectionClass($emailfactorclass::class);
$rcm = $rc->getMethod('check_verification_code');
$rcm->setAccessible(true);
// Assigned email to be used in getting the email factor.
$USER->email = '[email protected]';
set_config('enabled', 1, 'factor_email');
// Testing with current timecreated.
$newcode = random_int(100000, 999999);
$instanceid = $DB->insert_record('tool_mfa', [
'userid' => $USER->id,
'factor' => 'email',
'secret' => $newcode,
'label' => 'unittest',
'timecreated' => time(),
'timemodified' => time(),
'lastverified' => time(),
'revoked' => 0,
]);
$data = $DB->get_record('tool_mfa', ['id' => $instanceid]);
$this->assertTrue($rcm->invoke($emailfactorclass, $data->secret));
// Update the data to test with really old timecreated.
$DB->update_record('tool_mfa', [
'id' => $instanceid,
'timecreated' => time() - 1689657581,
'timemodified' => time() - 1689657581,
'lastverified' => time() - 1689657581,
'revoked' => 0,
]);
$data = $DB->get_record('tool_mfa', ['id' => $instanceid]);
$this->assertFalse($rcm->invoke($emailfactorclass, $data->secret));
// Cleans up email records once MFA passed.
$rcm = $rc->getMethod('post_pass_state');
$rcm->setAccessible(true);
$rcm->invoke($emailfactorclass);
// Check if the email records have been deleted.
$data = $DB->count_records('tool_mfa', ['factor' => 'email']);
$this->assertEquals(0, $data);
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_email';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+13 -11
View File
@@ -16,6 +16,7 @@
namespace factor_grace;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -63,7 +64,7 @@ class factor extends object_factor_base {
* @param stdClass $user object to check against.
* @return array the array of active factors.
*/
public function get_active_user_factors($user) {
public function get_active_user_factors(stdClass $user): array {
return $this->get_all_user_factors($user);
}
@@ -73,7 +74,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -84,7 +85,7 @@ class factor extends object_factor_base {
* @param bool $redirectable should this state call be allowed to redirect the user?
* @return string state constant
*/
public function get_state($redirectable = true) {
public function get_state($redirectable = true): string {
global $FULLME, $SESSION, $USER;
$records = ($this->get_all_user_factors($USER));
$record = reset($records);
@@ -154,10 +155,10 @@ class factor extends object_factor_base {
* Grace Factor implementation.
* State cannot be set. Return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -167,7 +168,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function post_pass_state() {
public function post_pass_state(): void {
global $USER;
parent::post_pass_state();
@@ -206,7 +207,7 @@ class factor extends object_factor_base {
* @param array $combination array of factors that make up the combination
* @return bool
*/
public function check_combination($combination) {
public function check_combination(array $combination): bool {
// If this combination has more than 1 factor that has setup or input, not valid.
foreach ($combination as $factor) {
if ($factor->has_setup() || $factor->has_input()) {
@@ -220,9 +221,10 @@ class factor extends object_factor_base {
* Grace Factor implementation.
* Gracemode can change outcome just by waiting, or based on other factors.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -236,7 +238,7 @@ class factor extends object_factor_base {
*
* @return array
*/
public function get_no_redirect_urls() {
public function get_no_redirect_urls(): array {
$redirect = get_config('factor_grace', 'forcesetup');
// First check if user has any other input or setup factors active.
@@ -17,7 +17,6 @@
namespace factor_grace\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -35,7 +35,7 @@ class revoke_expired_factors extends \core\task\scheduled_task {
*
* @return string
*/
public function get_name() {
public function get_name(): string {
return get_string('revokeexpiredfactors', 'factor_grace');
}
@@ -44,7 +44,7 @@ class revoke_expired_factors extends \core\task\scheduled_task {
*
* @return void
*/
public function execute() {
public function execute(): void {
mtrace('Starting to revoke expired Grace factors');
$this->revoke_factors();
}
@@ -54,7 +54,7 @@ class revoke_expired_factors extends \core\task\scheduled_task {
*
* @return void
*/
private function revoke_factors() {
private function revoke_factors(): void {
global $DB;
// If config is not set, pull out.
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace factor_grace\tests;
namespace factor_grace;
/**
* Tests for grace factor.
@@ -26,6 +26,12 @@ namespace factor_grace\tests;
*/
class factor_test extends \advanced_testcase {
/**
* Test affecting factors
*
* @covers ::get_affecting_factors
* @return void
*/
public function test_affecting_factors() {
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022020401; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_grace';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -16,6 +16,7 @@
namespace factor_iprange;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
$safeips = get_config('factor_iprange', 'safeips');
// TODO: Check for failures here.
@@ -90,10 +91,10 @@ class factor extends object_factor_base {
* IP Range Factor implementation.
* Cannot set state, return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -102,9 +103,9 @@ class factor extends object_factor_base {
* User can influence state prior to login.
* Possible states are either neutral or pass.
*
* @param \stdClass $user
* @param stdClass $user
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -17,7 +17,6 @@
namespace factor_iprange\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+3 -3
View File
@@ -25,9 +25,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_iprange';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -16,6 +16,7 @@
namespace factor_nosetup;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
// Check if user has any other input or setup factors active.
$factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types();
foreach ($factors as $factor) {
@@ -91,7 +92,7 @@ class factor extends object_factor_base {
* @param stdClass $user
* @return void
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
// We return Neutral here because to support optional rollouts
// it needs to report neutral or the menu to setup will not display.
return [\tool_mfa\plugininfo\factor::STATE_NEUTRAL];
@@ -101,10 +102,10 @@ class factor extends object_factor_base {
* No Setup Factor implementation.
* The state can never be set. Always return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -115,7 +116,7 @@ class factor extends object_factor_base {
* @param array $combination array of factors that make up the combination
* @return bool
*/
public function check_combination($combination) {
public function check_combination(array $combination): bool {
// If this combination has more than 1 factor that has setup or input, not valid.
foreach ($combination as $factor) {
if ($factor->has_setup() || $factor->has_input()) {
@@ -17,7 +17,6 @@
namespace factor_nosetup\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -35,7 +35,7 @@ class delete_unusable_factors extends \core\task\scheduled_task {
*
* @return string
*/
public function get_name() {
public function get_name(): string {
return get_string('deleteunusablefactors', 'factor_nosetup');
}
@@ -44,7 +44,7 @@ class delete_unusable_factors extends \core\task\scheduled_task {
*
* @return void
*/
public function execute() {
public function execute(): void {
mtrace('Starting to revoke unusable Nosetup factors');
$this->revoke_factors();
}
@@ -54,7 +54,7 @@ class delete_unusable_factors extends \core\task\scheduled_task {
*
* @return void
*/
private function revoke_factors() {
private function revoke_factors(): void {
global $DB;
$factorobject = \tool_mfa\plugininfo\factor::get_factor('nosetup');
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2020042302; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_nosetup';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+42 -21
View File
@@ -16,6 +16,7 @@
namespace factor_role;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -35,7 +36,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -62,7 +63,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$rolestring = get_config('factor_role', 'roles');
@@ -114,10 +115,10 @@ class factor extends object_factor_base {
* Role implementation.
* Cannot set state, return true.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state(string $state): bool {
return true;
}
@@ -125,9 +126,10 @@ class factor extends object_factor_base {
* Role implementation.
* User can not influence. Result is whatever current state is.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [$this->get_state()];
}
@@ -137,27 +139,46 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_summary_condition() {
global $DB;
public function get_summary_condition(): string {
$selectedroles = get_config('factor_role', 'roles');
if (empty($selectedroles)) {
return get_string('summarycondition', 'factor_role', get_string('none'));
} else {
$selectedroles = explode(',', $selectedroles);
}
$names = [];
foreach ($selectedroles as $role) {
if ($role === 'admin') {
$names[] = get_string('administrator');
} else {
$record = $DB->get_record('role', ['id' => $role]);
$names[] = role_get_name($record);
$selectedroles = $this->get_roles(explode(',', $selectedroles));
if (empty($selectedroles)) {
return get_string('summarycondition', 'factor_role', get_string('none'));
}
return get_string('summarycondition', 'factor_role', implode(', ', $selectedroles));
}
/**
* Get array of the selected role name.
*
* @param array $selectedroles
* @return array
*/
public function get_roles(array $selectedroles) : array {
global $DB;
$roles = [];
// Checks for admin role and gets its role name.
if (($key = array_search('admin', $selectedroles)) !== false) {
$roles[] = get_string('administrator');
unset($selectedroles[$key]);
}
// Gets role name for all non admin roles.
if (count($selectedroles) > 0) {
[$insql, $inparams] = $DB->get_in_or_equal($selectedroles);
$otherroles = $DB->get_records_select('role', 'id ' . $insql, $inparams);
foreach ($otherroles as $role) {
$roles[] = role_get_name($role);
}
}
$string = implode(', ', $names);
return get_string('summarycondition', 'factor_role', $string);
return $roles;
}
}
@@ -17,7 +17,6 @@
namespace factor_role\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,104 @@
<?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/>.
namespace factor_role;
/**
* Tests for role factor.
*
* @covers \factor_role\factor
* @package factor_role
* @copyright 2023 Stevani Andolo <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class factor_test extends \advanced_testcase {
/**
* Tests getting the summary condition
*
* @covers ::get_summary_condition
* @covers ::get_roles
*/
public function test_get_summary_condition() {
global $DB;
$this->resetAfterTest();
set_config('enabled', 1, 'factor_role');
$rolefactor = \tool_mfa\plugininfo\factor::get_factor('role');
// Admin is disabled by default in this factor.
$selectedroles = get_config('factor_role', 'roles');
$selectedroles = $rolefactor->get_roles(explode(',', $selectedroles));
$this->assertStringContainsString(
implode(', ', $selectedroles),
$rolefactor->get_summary_condition()
);
// Disabled role factor for managers.
$managerrole = $DB->get_record('role', ['shortname' => 'manager']);
set_config('roles', $managerrole->id, 'factor_role');
$selectedroles = get_config('factor_role', 'roles');
$selectedroles = $rolefactor->get_roles(explode(',', $selectedroles));
$this->assertStringContainsString(
implode(', ', $selectedroles),
$rolefactor->get_summary_condition()
);
// Disabled role factor for teachers.
$teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
set_config('roles', $teacherrole->id, 'factor_role');
$selectedroles = get_config('factor_role', 'roles');
$selectedroles = $rolefactor->get_roles(explode(',', $selectedroles));
$this->assertStringContainsString(
implode(', ', $selectedroles),
$rolefactor->get_summary_condition()
);
// Disabled role factor for students.
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
set_config('roles', $studentrole->id, 'factor_role');
$selectedroles = get_config('factor_role', 'roles');
$selectedroles = $rolefactor->get_roles(explode(',', $selectedroles));
$this->assertStringContainsString(
implode(', ', $selectedroles),
$rolefactor->get_summary_condition()
);
// Disabled role factor for admins, managers, teachers and students.
$managerrole = $DB->get_record('role', ['shortname' => 'manager']);
$teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
set_config('roles', "admin,$managerrole->id,$teacherrole->id,$studentrole->id", 'factor_role');
$selectedroles = get_config('factor_role', 'roles');
$selectedroles = $rolefactor->get_roles(explode(',', $selectedroles));
$this->assertStringContainsString(
implode(', ', $selectedroles),
$rolefactor->get_summary_condition()
);
// Enable all roles.
unset_config('roles', 'factor_role');
$this->assertEquals(
get_string('summarycondition', 'factor_role', get_string('none')),
$rolefactor->get_summary_condition()
);
}
}
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2020072100; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_role';
$plugin->release = 'v0.1';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
@@ -15,6 +15,9 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace factor_token\event;
use stdClass;
/**
* Event for a token being created for a user.
*
@@ -31,11 +34,11 @@ class token_created extends \core\event\base {
* @param stdClass $user the User object of the User who had the token creeated.
* @param array $state an array of the state of the token.
*
* @return token_created the token_created_event event
* @return \core\event\base the token_created_event event
*
* @throws \coding_exception
*/
public static function token_created_event($user, $state) {
public static function token_created_event(stdClass $user, array $state): \core\event\base {
$data = [
'relateduserid' => $user->id,
'context' => \context_user::instance($user->id),
@@ -53,7 +56,7 @@ class token_created extends \core\event\base {
*
* @return void
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
@@ -63,7 +66,7 @@ class token_created extends \core\event\base {
*
* @return string
*/
public function get_description() {
public function get_description(): string {
$info = json_decode($this->other['state']);
$string = '<br>';
foreach ($info as $name => $value) {
@@ -74,7 +77,10 @@ class token_created extends \core\event\base {
$string .= ucwords($name) . ': ' . $value . '<br>';
}
return "The user with id '{$this->other['userid']}' had an MFA token stored on their device. <br> Information:" . $string;
$data = new stdClass();
$data->string = $string;
$data->userid = $this->other['userid'];
return get_string('tokenstoredindevice', 'factor_token', $data);
}
/**
@@ -83,7 +89,7 @@ class token_created extends \core\event\base {
* @return string
* @throws \coding_exception
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:token_created', 'factor_token');
}
}
+13 -10
View File
@@ -16,6 +16,7 @@
namespace factor_token;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
use tool_mfa\local\secret_manager;
@@ -34,7 +35,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return false;
}
@@ -45,7 +46,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
$records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
@@ -72,7 +73,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
// Check if there was a previous locked status to return.
@@ -108,10 +109,10 @@ class factor extends object_factor_base {
* Token Implementation.
* We can't get_state like the parent here or it will recurse forever.
*
* @param mixed $state the state constant to set
* @param string $state the state constant to set
* @return bool
*/
public function set_state($state) {
public function set_state($state): bool {
global $SESSION;
$property = 'factor_' . $this->name;
$SESSION->$property = $state;
@@ -121,9 +122,10 @@ class factor extends object_factor_base {
/**
* Token implementation.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -138,7 +140,7 @@ class factor extends object_factor_base {
* @param \MoodleQuickForm $mform Form to inject global elements into.
* @return void
*/
public function global_definition_after_data($mform) {
public function global_definition_after_data($mform): void {
global $SESSION;
// First thing, we need to decide on whether we should show the checkbox.
@@ -159,8 +161,9 @@ class factor extends object_factor_base {
* Store information about the token status.
*
* @param object $data Data from the form.
* @return void
*/
public function global_submit($data) {
public function global_submit($data): void {
global $SESSION;
// Store any kind of response here, we shouldnt show again.
@@ -174,7 +177,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function post_pass_state() {
public function post_pass_state(): void {
global $CFG, $SESSION, $USER;
if (!property_exists($SESSION, 'tool_mfa_factor_token')) {
@@ -17,7 +17,6 @@
namespace factor_token\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -32,3 +32,4 @@ $string['settings:expireovernight_help'] = 'This forces tokens to expire overnig
$string['settings:expiry'] = 'Trust duration';
$string['settings:expiry_help'] = 'The duration a device is trusted before requiring a new MFA authentication.';
$string['summarycondition'] = 'the user has previously trusted this device';
$string['tokenstoredindevice'] = 'The user with id {$a->userid} had an MFA token stored on their device. <br> Information: {$a->string}.';
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace factor_token\tests;
namespace factor_token;
/**
* Tests for MFA manager class.
@@ -27,11 +27,24 @@ namespace factor_token\tests;
*/
class factor_test extends \advanced_testcase {
/**
* Holds specific requested factor, which is token factor.
*
* @var \factor_token\factor $factor
*/
public \factor_token\factor $factor;
public function setUp(): void {
$this->resetAfterTest();
$this->factor = new \factor_token\factor('token');
}
/**
* Test calculating expiry time in general
*
* @covers ::calculate_expiry_time
* @return void
*/
public function test_calculate_expiry_time_in_general() {
$timestamp = 1642213800; // 1230 UTC.
@@ -42,7 +55,7 @@ class factor_test extends \advanced_testcase {
// Test that non-overnight timestamps are just exactly as configured.
// We don't need to care about 0 or negative ints, they will just make the cookie expire immediately.
$expiry = $method->invoke($this->factor, $timestamp);
$this->assertEquals($expiry[1], DAYSECS);
$this->assertEquals(DAYSECS, $expiry[1]);
set_config('expiry', HOURSECS, 'factor_token');
$expiry = $method->invoke($this->factor, $timestamp);
@@ -75,6 +88,7 @@ class factor_test extends \advanced_testcase {
* value, provided it never goes past raw value expiry time, and when it
* needs to be 2am, it's 2am on the following morning.
*
* @covers ::calculate_expiry_time
* @param int $timestamp
* @dataProvider timestamp_provider
*/
@@ -123,6 +137,7 @@ class factor_test extends \advanced_testcase {
* value, provided it never goes past raw value expiry time, and when it
* needs to be 2am, it's 2am on the morning after tomorrow.
*
* @covers ::calculate_expiry_time
* @param int $timestamp
* @dataProvider timestamp_provider
*/
@@ -173,6 +188,7 @@ class factor_test extends \advanced_testcase {
/**
* This should check if the 3am expiry is pushed back to 2am as expected, but everything else appears as expected
*
* @covers ::calculate_expiry_time
* @param int $timestamp
* @dataProvider timestamp_provider
*/
@@ -217,6 +233,7 @@ class factor_test extends \advanced_testcase {
/**
* Only relevant based on the hour padding used, which is currently set to 2 hours (2am).
*
* @covers ::calculate_expiry_time
* @param int $timestamp
* @dataProvider timestamp_provider
*/
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022011700; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_token';
$plugin->release = 2022011700;
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+27 -26
View File
@@ -34,6 +34,7 @@ require_once(__DIR__.'/../extlib/ParagonIE/ConstantTime/Base32.php');
use tool_mfa\local\factor\object_factor_base;
use OTPHP\TOTP;
use stdClass;
/**
* TOTP factor class.
@@ -69,7 +70,7 @@ class factor extends object_factor_base {
* @param string $secret
* @return string
*/
public function generate_totp_uri($secret) {
public function generate_totp_uri(string $secret): string {
global $USER, $SITE, $CFG;
$host = parse_url($CFG->wwwroot, PHP_URL_HOST);
$sitename = str_replace(':', '', $SITE->fullname);
@@ -86,7 +87,7 @@ class factor extends object_factor_base {
* @param string $secret
* @return string
*/
public function generate_qrcode($secret) {
public function generate_qrcode(string $secret): string {
$uri = $this->generate_totp_uri($secret);
$qrcode = new \TCPDF2DBarcode($uri, 'QRCODE');
$image = $qrcode->getBarcodePngData(7, 7);
@@ -100,7 +101,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$userfactors = $this->get_active_user_factors($USER);
@@ -116,9 +117,9 @@ class factor extends object_factor_base {
* TOTP Factor implementation.
*
* @param \MoodleQuickForm $mform
* @return object $mform
* @return \MoodleQuickForm $mform
*/
public function setup_factor_form_definition($mform) {
public function setup_factor_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
$secret = $this->generate_secret_code();
$mform->addElement('hidden', 'secret', $secret);
$mform->setType('secret', PARAM_ALPHANUM);
@@ -130,12 +131,12 @@ class factor extends object_factor_base {
* TOTP Factor implementation.
*
* @param \MoodleQuickForm $mform
* @return object $mform
* @return \MoodleQuickForm $mform
*/
public function setup_factor_form_definition_after_data($mform) {
public function setup_factor_form_definition_after_data(\MoodleQuickForm $mform): \MoodleQuickForm {
global $OUTPUT, $SITE, $USER;
// Array of elements to allow XSS on when we're running Totara.
// Array of elements to allow XSS.
$xssallowedelements = [];
$mform->addElement('html', $OUTPUT->heading(get_string('setupfactor', 'factor_totp'), 2));
@@ -199,7 +200,7 @@ class factor extends object_factor_base {
$html = $togglelink . $html;
$xssallowedelements[] = $mform->addElement('static', 'enter', '', $html);
// Allow XSS on Totara.
// Allow XSS.
if (method_exists('MoodleQuickForm_static', 'set_allow_xss')) {
foreach ($xssallowedelements as $xssallowedelement) {
$xssallowedelement->set_allow_xss(true);
@@ -220,7 +221,7 @@ class factor extends object_factor_base {
* @param array $data
* @return array
*/
public function setup_factor_form_validation($data) {
public function setup_factor_form_validation(array $data): array {
$errors = [];
$totp = TOTP::create($data['secret']);
@@ -235,9 +236,9 @@ class factor extends object_factor_base {
* TOTP Factor implementation.
*
* @param \MoodleQuickForm $mform
* @return object $mform
* @return \MoodleQuickForm $mform
*/
public function login_form_definition($mform) {
public function login_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
$mform->disable_form_change_checker();
$mform->addElement(new \tool_mfa\local\form\verification_field());
@@ -253,7 +254,7 @@ class factor extends object_factor_base {
* @param array $data
* @return array
*/
public function login_form_validation($data) {
public function login_form_validation(array $data): array {
global $USER;
$factors = $this->get_active_user_factors($USER);
$result = ['verificationcode' => get_string('error:wrongverification', 'factor_totp')];
@@ -295,9 +296,9 @@ class factor extends object_factor_base {
* @param TOTP $totp the totp object to check against.
* @param stdClass $factor the factor with information required.
*
* @return const constant with verification state.
* @return string constant with verification state.
*/
public function validate_code($code, $window, $totp, $factor) {
public function validate_code(string $code, int $window, TOTP $totp, stdClass $factor): string {
// First check if this code matches the last verified timestamp.
$lastverified = $this->get_lastverified($factor->id);
if ($lastverified > 0 && $totp->verify($code, $lastverified, $window)) {
@@ -328,7 +329,7 @@ class factor extends object_factor_base {
*
* @return string
*/
public function generate_secret_code() {
public function generate_secret_code(): string {
$totp = TOTP::create();
return substr($totp->getSecret(), 0, 16);
}
@@ -339,11 +340,11 @@ class factor extends object_factor_base {
* @param stdClass $data
* @return stdClass the factor record, or null.
*/
public function setup_user_factor($data) {
public function setup_user_factor(stdClass $data): stdClass|null {
global $DB, $USER;
if (!empty($data->secret)) {
$row = new \stdClass();
$row = new stdClass();
$row->userid = $USER->id;
$row->factor = $this->name;
$row->secret = $data->secret;
@@ -381,7 +382,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors($user): array {
global $DB;
return $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
}
@@ -391,7 +392,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_revoke() {
public function has_revoke(): bool {
return true;
}
@@ -400,7 +401,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_setup() {
public function has_setup(): bool {
return true;
}
@@ -409,7 +410,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function show_setup_buttons() {
public function show_setup_buttons(): bool {
return true;
}
@@ -419,7 +420,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function post_pass_state() {
public function post_pass_state(): void {
return;
}
@@ -427,9 +428,9 @@ class factor extends object_factor_base {
* TOTP Factor implementation.
* TOTP cannot return fail state.
*
* @param \stdClass $user
* @param stdClass $user
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -442,7 +443,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_setup_string() {
public function get_setup_string(): string {
return get_string('factorsetup', 'factor_totp');
}
}
@@ -17,7 +17,6 @@
namespace factor_totp\privacy;
use core_privacy\local\metadata\null_provider;
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider.
@@ -28,7 +27,6 @@ use core_privacy\local\legacy_polyfill;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
@@ -36,7 +34,7 @@ class provider implements null_provider {
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,11 @@
Copyright (c) 2011-2013, Benjamin Eberlei
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
@@ -0,0 +1,23 @@
{
"name": "beberlei/assert",
"description": "Thin assertion library for input validation in business models.",
"authors": [
{"name": "Benjamin Eberlei", "email": "[email protected]"}
],
"license": "BSD-2-Clause",
"keywords": ["assert", "assertion", "validation"],
"require": {
"ext-mbstring": "*"
},
"autoload": {
"psr-0": {
"Assert": "lib/"
},
"files": ["lib/Assert/functions.php"]
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
}
}
@@ -0,0 +1,17 @@
Assert 2.1
--------------
https://github.com/beberlei/assert/releases/tag/v2.1
Instructions to import WebAuthn into Moodle:
1. Download the latest release from https://github.com/beberlei/assert/releases/tag/vx.x
(choose "Source code")
2. Unzip the source code
3. Copy the following files from assert-x.x/lib/Assert into admin/tool/mfa/factor/totp/extlib/Assert:
1. Assertion.php
2. AssertionFailedException.php
3. InvalidArgumentException.php
4. Copy the following files from assert-x.x into admin/tool/mfa/factor/totp/extlib/Assert:
1. LICENSE
2. composer.json
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Florent Morselli
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
{
"name": "spomky-labs/otphp",
"type": "library",
"description": "A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator",
"license": "MIT",
"keywords": ["otp", "hotp", "totp", "RFC 4226", "RFC 6238", "Google Authenticator", "FreeOTP"],
"homepage": "https://github.com/Spomky-Labs/otphp",
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky"
},
{
"name": "All contributors",
"homepage": "https://github.com/Spomky-Labs/otphp/contributors"
}
],
"require": {
"php": "^7.1",
"paragonie/constant_time_encoding": "^2.0",
"beberlei/assert": "^2.4"
},
"require-dev": {
"phpunit/phpunit": "^6.0",
"satooshi/php-coveralls": "^1.0"
},
"suggest": {
},
"autoload": {
"psr-4": { "OTPHP\\": "src/" }
},
"autoload-dev": {
"psr-4": { "OTPHP\\Test\\": "tests/" }
},
"extra": {
"branch-alias": {
"dev-master": "9.0.x-dev"
}
}
}
@@ -0,0 +1,19 @@
OTPHP 9.1.1
--------------
https://github.com/Spomky-Labs/otphp/releases/tag/v9.1.1
Instructions to import WebAuthn into Moodle:
1. Download the latest release from https://github.com/Spomky-Labs/otphp/releases/tag/vx.x.x
(choose "Source code")
2. Unzip the source code
3. Copy the following files from otphp-x.x/lib/OTPHP into admin/tool/mfa/factor/totp/extlib/OTPHP:
1. OTP.php
2. OTPInterface.php
3. ParameterTrait.php
4. TOTP.php
5. TOTPInterface.php
4. Copy the following files from otphp-x.x into admin/tool/mfa/factor/totp/extlib/OTPHP:
1. LICENSE
2. composer.json
@@ -0,0 +1,48 @@
The MIT License (MIT)
Copyright (c) 2016 Paragon Initiative Enterprises
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
This library was based on the work of Steve "Sc00bz" Thomas.
------------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2014 Steve Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,40 @@
{
"name": "paragonie/constant_time_encoding",
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base64", "encoding", "rfc4648", "base32", "base16", "hex", "bin2hex", "hex2bin", "base64_encode", "base64_decode", "base32_encode", "base32_decode"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "[email protected]",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "[email protected]",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"support": {
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"email": "[email protected]",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"require": {
"php": "^7"
},
"require-dev": {
"phpunit/phpunit": "^6",
"vimeo/psalm": "^0.3|^"
},
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
}
}
@@ -0,0 +1,17 @@
Constant-Time Encoding 2.1.1
--------------
https://github.com/paragonie/constant_time_encoding/releases/tag/v2.1.1
Instructions to import WebAuthn into Moodle:
1. Download the latest release from https://github.com/paragonie/constant_time_encoding/releases/tag/vx.x.x
(choose "Source code")
2. Unzip the source code
3. Copy the following files from constant_time_encoding-x.x/lib/constant_time_encoding into admin/tool/mfa/factor/totp/extlib/ParagonIE/ConstantTime:
1. Base32.php
2. Binary.php
3. EncoderInterface.php
4. Copy the following files from constant_time_encoding-x.x into admin/tool/mfa/factor/totp/extlib/ParagonIE/ConstantTime:
1. LICENSE
2. composer.json
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace factor_totp\tests;
namespace factor_totp;
defined('MOODLE_INTERNAL') || die();
@@ -3,22 +3,22 @@
<library>
<location>extlib/Assert</location>
<name>Assert</name>
<version></version>
<version>2.1</version>
<license>MIT</license>
<licenseversion>2.1+</licenseversion>
<repository>https://github.com/beberlei/assert</repository>
</library>
<library>
<location>extlib/OTPHP</location>
<name>OTPHP</name>
<version></version>
<version>9.1.1</version>
<license>MIT</license>
<licenseversion>2.1+</licenseversion>
<repository>https://github.com/Spomky-Labs/otphp</repository>
</library>
<library>
<location>extlib/ParagonIE</location>
<name>ParagonIE</name>
<version></version>
<license>Custom</license>
<licenseversion></licenseversion>
<location>extlib/ParagonIE/ConstantTime</location>
<name>Constant-Time Encoding</name>
<version>2.1.1</version>
<license>MIT</license>
<repository>https://github.com/paragonie/constant_time_encoding</repository>
</library>
</libraries>
+3 -3
View File
@@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2021021700; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->release = 2021021700;
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_totp';
$plugin->maturity = MATURITY_STABLE;
$plugin->dependencies = ['tool_mfa' => 2019102400];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+1 -1
View File
@@ -6,6 +6,6 @@
* @author Alex Morris <alex.morris@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("factor_webauthn/login",["factor_webauthn/utils"],(function(utils){return{init:function(getArgs){document.getElementById("id_submitbutton").addEventListener("click",(async function(e){if(e.preventDefault(),!navigator.credentials||!navigator.credentials.create)throw new Error("Browser not supported.");if(!1===(getArgs=JSON.parse(getArgs)).success)throw new Error(getArgs.msg||"unknown error occured");utils.recursiveBase64StrToArrayBuffer(getArgs);const cred=await navigator.credentials.get(getArgs),authenticatorAttestationResponse={id:cred.rawId?utils.arrayBufferToBase64(cred.rawId):null,clientDataJSON:cred.response.clientDataJSON?utils.arrayBufferToBase64(cred.response.clientDataJSON):null,authenticatorData:cred.response.authenticatorData?utils.arrayBufferToBase64(cred.response.authenticatorData):null,signature:cred.response.signature?utils.arrayBufferToBase64(cred.response.signature):null,userHandle:cred.response.userHandle?utils.arrayBufferToBase64(cred.response.userHandle):null};document.getElementById("id_response_input").value=JSON.stringify(authenticatorAttestationResponse),document.getElementById("id_response_input").form.submit()}))}}}));
define("factor_webauthn/login",["factor_webauthn/utils"],(function(utils){return{init:function(getArgs){const idSubmitButton=document.getElementById("id_submitbutton");idSubmitButton&&idSubmitButton.addEventListener("click",(async function(e){if(e.preventDefault(),!navigator.credentials||!navigator.credentials.create)throw new Error("Browser not supported.");if(!1===(getArgs=JSON.parse(getArgs)).success)throw new Error(getArgs.msg||"unknown error occured");utils.recursiveBase64StrToArrayBuffer(getArgs);const cred=await navigator.credentials.get(getArgs),authenticatorAttestationResponse={id:cred.rawId?utils.arrayBufferToBase64(cred.rawId):null,clientDataJSON:cred.response.clientDataJSON?utils.arrayBufferToBase64(cred.response.clientDataJSON):null,authenticatorData:cred.response.authenticatorData?utils.arrayBufferToBase64(cred.response.authenticatorData):null,signature:cred.response.signature?utils.arrayBufferToBase64(cred.response.signature):null,userHandle:cred.response.userHandle?utils.arrayBufferToBase64(cred.response.userHandle):null},responseInput=document.getElementById("id_response_input");responseInput.value=JSON.stringify(authenticatorAttestationResponse),responseInput.form.submit()}))}}}));
//# sourceMappingURL=login.min.js.map
@@ -1 +1 @@
{"version":3,"file":"login.min.js","sources":["../src/login.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * For collecting WebAuthn authenticator details on login\n *\n * @module factor_webauthn/login\n * @copyright Catalyst IT\n * @author Alex Morris <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['factor_webauthn/utils'], function(utils) {\n return {\n init: function(getArgs) {\n document.getElementById('id_submitbutton').addEventListener('click', async function(e) {\n e.preventDefault();\n if (!navigator.credentials || !navigator.credentials.create) {\n throw new Error('Browser not supported.');\n }\n\n getArgs = JSON.parse(getArgs);\n\n if (getArgs.success === false) {\n throw new Error(getArgs.msg || 'unknown error occured');\n }\n\n utils.recursiveBase64StrToArrayBuffer(getArgs);\n\n const cred = await navigator.credentials.get(getArgs);\n\n const authenticatorAttestationResponse = {\n id: cred.rawId ? utils.arrayBufferToBase64(cred.rawId) : null,\n clientDataJSON: cred.response.clientDataJSON ? utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,\n authenticatorData:\n cred.response.authenticatorData ? utils.arrayBufferToBase64(cred.response.authenticatorData) : null,\n signature: cred.response.signature ? utils.arrayBufferToBase64(cred.response.signature) : null,\n userHandle: cred.response.userHandle ? utils.arrayBufferToBase64(cred.response.userHandle) : null\n };\n\n document.getElementById('id_response_input').value = JSON.stringify(authenticatorAttestationResponse);\n document.getElementById('id_response_input').form.submit();\n });\n }\n };\n});\n"],"names":["define","utils","init","getArgs","document","getElementById","addEventListener","async","e","preventDefault","navigator","credentials","create","Error","JSON","parse","success","msg","recursiveBase64StrToArrayBuffer","cred","get","authenticatorAttestationResponse","id","rawId","arrayBufferToBase64","clientDataJSON","response","authenticatorData","signature","userHandle","value","stringify","form","submit"],"mappings":";;;;;;;;AAwBAA,+BAAO,CAAC,0BAA0B,SAASC,aAChC,CACHC,KAAM,SAASC,SACXC,SAASC,eAAe,mBAAmBC,iBAAiB,SAASC,eAAeC,MAChFA,EAAEC,kBACGC,UAAUC,cAAgBD,UAAUC,YAAYC,aAC3C,IAAIC,MAAM,8BAKI,KAFxBV,QAAUW,KAAKC,MAAMZ,UAETa,cACF,IAAIH,MAAMV,QAAQc,KAAO,yBAGnChB,MAAMiB,gCAAgCf,eAEhCgB,WAAaT,UAAUC,YAAYS,IAAIjB,SAEvCkB,iCAAmC,CACrCC,GAAIH,KAAKI,MAAQtB,MAAMuB,oBAAoBL,KAAKI,OAAS,KACzDE,eAAgBN,KAAKO,SAASD,eAAiBxB,MAAMuB,oBAAoBL,KAAKO,SAASD,gBAAkB,KACzGE,kBACIR,KAAKO,SAASC,kBAAoB1B,MAAMuB,oBAAoBL,KAAKO,SAASC,mBAAqB,KACnGC,UAAWT,KAAKO,SAASE,UAAY3B,MAAMuB,oBAAoBL,KAAKO,SAASE,WAAa,KAC1FC,WAAYV,KAAKO,SAASG,WAAa5B,MAAMuB,oBAAoBL,KAAKO,SAASG,YAAc,MAGjGzB,SAASC,eAAe,qBAAqByB,MAAQhB,KAAKiB,UAAUV,kCACpEjB,SAASC,eAAe,qBAAqB2B,KAAKC"}
{"version":3,"file":"login.min.js","sources":["../src/login.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * For collecting WebAuthn authenticator details on login\n *\n * @module factor_webauthn/login\n * @copyright Catalyst IT\n * @author Alex Morris <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['factor_webauthn/utils'], function(utils) {\n return {\n init: function(getArgs) {\n const idSubmitButton = document.getElementById('id_submitbutton');\n if (idSubmitButton) {\n idSubmitButton.addEventListener('click', async function(e) {\n e.preventDefault();\n if (!navigator.credentials || !navigator.credentials.create) {\n throw new Error('Browser not supported.');\n }\n\n getArgs = JSON.parse(getArgs);\n\n if (getArgs.success === false) {\n throw new Error(getArgs.msg || 'unknown error occured');\n }\n\n utils.recursiveBase64StrToArrayBuffer(getArgs);\n\n const cred = await navigator.credentials.get(getArgs);\n\n const authenticatorAttestationResponse = {\n id: cred.rawId ? utils.arrayBufferToBase64(cred.rawId) : null,\n clientDataJSON:\n cred.response.clientDataJSON ? utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,\n authenticatorData:\n cred.response.authenticatorData ? utils.arrayBufferToBase64(cred.response.authenticatorData) : null,\n signature: cred.response.signature ? utils.arrayBufferToBase64(cred.response.signature) : null,\n userHandle: cred.response.userHandle ? utils.arrayBufferToBase64(cred.response.userHandle) : null\n };\n\n const responseInput = document.getElementById('id_response_input');\n responseInput.value = JSON.stringify(authenticatorAttestationResponse);\n responseInput.form.submit();\n });\n }\n }\n };\n});\n"],"names":["define","utils","init","getArgs","idSubmitButton","document","getElementById","addEventListener","async","e","preventDefault","navigator","credentials","create","Error","JSON","parse","success","msg","recursiveBase64StrToArrayBuffer","cred","get","authenticatorAttestationResponse","id","rawId","arrayBufferToBase64","clientDataJSON","response","authenticatorData","signature","userHandle","responseInput","value","stringify","form","submit"],"mappings":";;;;;;;;AAwBAA,+BAAO,CAAC,0BAA0B,SAASC,aAChC,CACHC,KAAM,SAASC,eACLC,eAAiBC,SAASC,eAAe,mBAC3CF,gBACAA,eAAeG,iBAAiB,SAASC,eAAeC,MACpDA,EAAEC,kBACGC,UAAUC,cAAgBD,UAAUC,YAAYC,aAC3C,IAAIC,MAAM,8BAKI,KAFxBX,QAAUY,KAAKC,MAAMb,UAETc,cACF,IAAIH,MAAMX,QAAQe,KAAO,yBAGnCjB,MAAMkB,gCAAgChB,eAEhCiB,WAAaT,UAAUC,YAAYS,IAAIlB,SAEvCmB,iCAAmC,CACrCC,GAAIH,KAAKI,MAAQvB,MAAMwB,oBAAoBL,KAAKI,OAAS,KACzDE,eACIN,KAAKO,SAASD,eAAiBzB,MAAMwB,oBAAoBL,KAAKO,SAASD,gBAAkB,KAC7FE,kBACIR,KAAKO,SAASC,kBAAoB3B,MAAMwB,oBAAoBL,KAAKO,SAASC,mBAAqB,KACnGC,UAAWT,KAAKO,SAASE,UAAY5B,MAAMwB,oBAAoBL,KAAKO,SAASE,WAAa,KAC1FC,WAAYV,KAAKO,SAASG,WAAa7B,MAAMwB,oBAAoBL,KAAKO,SAASG,YAAc,MAG3FC,cAAgB1B,SAASC,eAAe,qBAC9CyB,cAAcC,MAAQjB,KAAKkB,UAAUX,kCACrCS,cAAcG,KAAKC"}
+1 -1
View File
@@ -6,6 +6,6 @@
* @author Alex Morris <alex.morris@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("factor_webauthn/utils",[],(function(){return{recursiveBase64StrToArrayBuffer:function(obj){if("object"==typeof obj)for(let key in obj)if("string"==typeof obj[key]){let str=obj[key];if("=?BINARY?B?"===str.substring(0,"=?BINARY?B?".length)&&"?="===str.substring(str.length-"?=".length)){str=str.substring("=?BINARY?B?".length,str.length-"?=".length);let binary_string=window.atob(str),len=binary_string.length,bytes=new Uint8Array(len);for(let i=0;i<len;i++)bytes[i]=binary_string.charCodeAt(i);obj[key]=bytes.buffer}}else this.recursiveBase64StrToArrayBuffer(obj[key])},arrayBufferToBase64:function(buffer){let binary="",bytes=new Uint8Array(buffer),len=bytes.byteLength;for(let i=0;i<len;i++)binary+=String.fromCharCode(bytes[i]);return window.btoa(binary)}}}));
define("factor_webauthn/utils",[],(function(){return{recursiveBase64StrToArrayBuffer:function(obj){if("object"==typeof obj)for(let key in obj){let isString=!0;"string"!=typeof obj[key]&&(this.recursiveBase64StrToArrayBuffer(obj[key]),isString=!1);let str=obj[key];if(isString&&"=?BINARY?B?"===str.substring(0,"=?BINARY?B?".length)&&"?="===str.substring(str.length-"?=".length)){str=str.substring("=?BINARY?B?".length,str.length-"?=".length);const binaryString=window.atob(str);let len=binaryString.length,bytes=new Uint8Array(len);for(let i=0;i<len;i++)bytes[i]=binaryString.charCodeAt(i);obj[key]=bytes.buffer}}},arrayBufferToBase64:function(buffer){let binary="",bytes=new Uint8Array(buffer),len=bytes.byteLength;for(let i=0;i<len;i++)binary+=String.fromCharCode(bytes[i]);return window.btoa(binary)}}}));
//# sourceMappingURL=utils.min.js.map
@@ -1 +1 @@
{"version":3,"file":"utils.min.js","sources":["../src/utils.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * WebAuthn utility functions, for handling array buffers.\n *\n * @module factor_webauthn/utils\n * @copyright Catalyst IT\n * @author Alex Morris <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([], function() {\n return {\n recursiveBase64StrToArrayBuffer: function(obj) {\n let prefix = '=?BINARY?B?';\n let suffix = '?=';\n if (typeof obj === 'object') {\n for (let key in obj) {\n if (typeof obj[key] === 'string') {\n let str = obj[key];\n if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) {\n str = str.substring(prefix.length, str.length - suffix.length);\n\n let binary_string = window.atob(str);\n let len = binary_string.length;\n let bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binary_string.charCodeAt(i);\n }\n obj[key] = bytes.buffer;\n }\n } else {\n this.recursiveBase64StrToArrayBuffer(obj[key]);\n }\n }\n }\n },\n arrayBufferToBase64: function(buffer) {\n let binary = '';\n let bytes = new Uint8Array(buffer);\n let len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return window.btoa(binary);\n },\n };\n});\n"],"names":["define","recursiveBase64StrToArrayBuffer","obj","key","str","substring","length","binary_string","window","atob","len","bytes","Uint8Array","i","charCodeAt","buffer","arrayBufferToBase64","binary","byteLength","String","fromCharCode","btoa"],"mappings":";;;;;;;;AAwBAA,+BAAO,IAAI,iBACA,CACHC,gCAAiC,SAASC,QAGnB,iBAARA,QACF,IAAIC,OAAOD,OACY,iBAAbA,IAAIC,KAAmB,KAC1BC,IAAMF,IAAIC,QALb,gBAMGC,IAAIC,UAAU,EANjB,cAM2BC,SAL3B,OAKiDF,IAAIC,UAAUD,IAAIE,OALnE,KAKmFA,QAAoB,CACpGF,IAAMA,IAAIC,UAPb,cAO8BC,OAAQF,IAAIE,OAN1C,KAM0DA,YAEnDC,cAAgBC,OAAOC,KAAKL,KAC5BM,IAAMH,cAAcD,OACpBK,MAAQ,IAAIC,WAAWF,SACtB,IAAIG,EAAI,EAAGA,EAAIH,IAAKG,IACrBF,MAAME,GAAKN,cAAcO,WAAWD,GAExCX,IAAIC,KAAOQ,MAAMI,kBAGhBd,gCAAgCC,IAAIC,OAKzDa,oBAAqB,SAASD,YACtBE,OAAS,GACTN,MAAQ,IAAIC,WAAWG,QACvBL,IAAMC,MAAMO,eACX,IAAIL,EAAI,EAAGA,EAAIH,IAAKG,IACrBI,QAAUE,OAAOC,aAAaT,MAAME,WAEjCL,OAAOa,KAAKJ"}
{"version":3,"file":"utils.min.js","sources":["../src/utils.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * WebAuthn utility functions, for handling array buffers.\n *\n * @module factor_webauthn/utils\n * @copyright Catalyst IT\n * @author Alex Morris <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([], function() {\n return {\n recursiveBase64StrToArrayBuffer: function(obj) {\n let prefix = '=?BINARY?B?';\n let suffix = '?=';\n if (typeof obj === 'object') {\n for (let key in obj) {\n let isString = true;\n if (typeof obj[key] !== 'string') {\n this.recursiveBase64StrToArrayBuffer(obj[key]);\n isString = false;\n }\n\n let str = obj[key];\n if (isString && str.substring(0, prefix.length) === prefix &&\n str.substring(str.length - suffix.length) === suffix) {\n str = str.substring(prefix.length, str.length - suffix.length);\n\n const binaryString = window.atob(str);\n let len = binaryString.length;\n let bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n obj[key] = bytes.buffer;\n }\n }\n }\n },\n arrayBufferToBase64: function(buffer) {\n let binary = '';\n let bytes = new Uint8Array(buffer);\n let len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return window.btoa(binary);\n },\n };\n});\n"],"names":["define","recursiveBase64StrToArrayBuffer","obj","key","isString","str","substring","length","binaryString","window","atob","len","bytes","Uint8Array","i","charCodeAt","buffer","arrayBufferToBase64","binary","byteLength","String","fromCharCode","btoa"],"mappings":";;;;;;;;AAwBAA,+BAAO,IAAI,iBACA,CACHC,gCAAiC,SAASC,QAGnB,iBAARA,QACF,IAAIC,OAAOD,IAAK,KACbE,UAAW,EACS,iBAAbF,IAAIC,YACNF,gCAAgCC,IAAIC,MACzCC,UAAW,OAGXC,IAAMH,IAAIC,QACVC,UAXC,gBAWWC,IAAIC,UAAU,EAXzB,cAWmCC,SAVnC,OAWAF,IAAIC,UAAUD,IAAIE,OAXlB,KAWkCA,QAAoB,CACvDF,IAAMA,IAAIC,UAbT,cAa0BC,OAAQF,IAAIE,OAZtC,KAYsDA,cAEjDC,aAAeC,OAAOC,KAAKL,SAC7BM,IAAMH,aAAaD,OACnBK,MAAQ,IAAIC,WAAWF,SACtB,IAAIG,EAAI,EAAGA,EAAIH,IAAKG,IACrBF,MAAME,GAAKN,aAAaO,WAAWD,GAEvCZ,IAAIC,KAAOS,MAAMI,UAKjCC,oBAAqB,SAASD,YACtBE,OAAS,GACTN,MAAQ,IAAIC,WAAWG,QACvBL,IAAMC,MAAMO,eACX,IAAIL,EAAI,EAAGA,EAAIH,IAAKG,IACrBI,QAAUE,OAAOC,aAAaT,MAAME,WAEjCL,OAAOa,KAAKJ"}
+27 -22
View File
@@ -25,34 +25,39 @@
define(['factor_webauthn/utils'], function(utils) {
return {
init: function(getArgs) {
document.getElementById('id_submitbutton').addEventListener('click', async function(e) {
e.preventDefault();
if (!navigator.credentials || !navigator.credentials.create) {
throw new Error('Browser not supported.');
}
const idSubmitButton = document.getElementById('id_submitbutton');
if (idSubmitButton) {
idSubmitButton.addEventListener('click', async function(e) {
e.preventDefault();
if (!navigator.credentials || !navigator.credentials.create) {
throw new Error('Browser not supported.');
}
getArgs = JSON.parse(getArgs);
getArgs = JSON.parse(getArgs);
if (getArgs.success === false) {
throw new Error(getArgs.msg || 'unknown error occured');
}
if (getArgs.success === false) {
throw new Error(getArgs.msg || 'unknown error occured');
}
utils.recursiveBase64StrToArrayBuffer(getArgs);
utils.recursiveBase64StrToArrayBuffer(getArgs);
const cred = await navigator.credentials.get(getArgs);
const cred = await navigator.credentials.get(getArgs);
const authenticatorAttestationResponse = {
id: cred.rawId ? utils.arrayBufferToBase64(cred.rawId) : null,
clientDataJSON: cred.response.clientDataJSON ? utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,
authenticatorData:
cred.response.authenticatorData ? utils.arrayBufferToBase64(cred.response.authenticatorData) : null,
signature: cred.response.signature ? utils.arrayBufferToBase64(cred.response.signature) : null,
userHandle: cred.response.userHandle ? utils.arrayBufferToBase64(cred.response.userHandle) : null
};
const authenticatorAttestationResponse = {
id: cred.rawId ? utils.arrayBufferToBase64(cred.rawId) : null,
clientDataJSON:
cred.response.clientDataJSON ? utils.arrayBufferToBase64(cred.response.clientDataJSON) : null,
authenticatorData:
cred.response.authenticatorData ? utils.arrayBufferToBase64(cred.response.authenticatorData) : null,
signature: cred.response.signature ? utils.arrayBufferToBase64(cred.response.signature) : null,
userHandle: cred.response.userHandle ? utils.arrayBufferToBase64(cred.response.userHandle) : null
};
document.getElementById('id_response_input').value = JSON.stringify(authenticatorAttestationResponse);
document.getElementById('id_response_input').form.submit();
});
const responseInput = document.getElementById('id_response_input');
responseInput.value = JSON.stringify(authenticatorAttestationResponse);
responseInput.form.submit();
});
}
}
};
});
+17 -14
View File
@@ -29,21 +29,24 @@ define([], function() {
let suffix = '?=';
if (typeof obj === 'object') {
for (let key in obj) {
if (typeof obj[key] === 'string') {
let str = obj[key];
if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) {
str = str.substring(prefix.length, str.length - suffix.length);
let binary_string = window.atob(str);
let len = binary_string.length;
let bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
obj[key] = bytes.buffer;
}
} else {
let isString = true;
if (typeof obj[key] !== 'string') {
this.recursiveBase64StrToArrayBuffer(obj[key]);
isString = false;
}
let str = obj[key];
if (isString && str.substring(0, prefix.length) === prefix &&
str.substring(str.length - suffix.length) === suffix) {
str = str.substring(prefix.length, str.length - suffix.length);
const binaryString = window.atob(str);
let len = binaryString.length;
let bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
obj[key] = bytes.buffer;
}
}
}
@@ -23,6 +23,7 @@ require_once($CFG->libdir . '/webauthn/src/WebAuthn.php');
use lbuchs\WebAuthn\Binary\ByteBuffer;
use lbuchs\WebAuthn\WebAuthn;
use lbuchs\WebAuthn\WebAuthnException;
use stdClass;
use tool_mfa\local\factor\object_factor_base;
/**
@@ -63,7 +64,7 @@ class factor extends object_factor_base {
* @param stdClass $user the user to check against.
* @return array
*/
public function get_all_user_factors($user) {
public function get_all_user_factors(stdClass $user): array {
global $DB;
return $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]);
}
@@ -73,7 +74,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_input() {
public function has_input(): bool {
return true;
}
@@ -82,7 +83,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_revoke() {
public function has_revoke(): bool {
return true;
}
@@ -91,7 +92,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function has_setup() {
public function has_setup(): bool {
return true;
}
@@ -100,16 +101,17 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function show_setup_buttons() {
public function show_setup_buttons(): bool {
return true;
}
/**
* WebAuthn factor implementation.
*
* @param \stdClass $user
* @param stdClass $user
* @return array
*/
public function possible_states($user) {
public function possible_states(stdClass $user): array {
return [
\tool_mfa\plugininfo\factor::STATE_PASS,
\tool_mfa\plugininfo\factor::STATE_NEUTRAL,
@@ -122,7 +124,7 @@ class factor extends object_factor_base {
*
* {@inheritDoc}
*/
public function get_state() {
public function get_state(): string {
global $USER;
$userfactors = $this->get_active_user_factors($USER);
@@ -136,8 +138,10 @@ class factor extends object_factor_base {
/**
* Gets the string for setup button on preferences page.
*
* @return string
*/
public function get_setup_string() {
public function get_setup_string(): string {
return get_string('setupfactor', 'factor_webauthn');
}
@@ -145,11 +149,13 @@ class factor extends object_factor_base {
* WebAuthn Factor implementation.
*
* @param \MoodleQuickForm $mform
* @return object $mform
* @return \MoodleQuickForm $mform
*/
public function login_form_definition($mform) {
public function login_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
global $PAGE, $USER, $SESSION;
$mform->addElement('html', get_string('loginexplanation', 'factor_webauthn'));
$mform->addElement('hidden', 'response_input', '', ['id' => 'id_response_input']);
$mform->setType('response_input', PARAM_RAW);
@@ -184,7 +190,7 @@ class factor extends object_factor_base {
* @param array $data
* @return array
*/
public function login_form_validation($data) {
public function login_form_validation(array $data): array {
global $USER, $SESSION;
$errors = [];
@@ -234,7 +240,7 @@ class factor extends object_factor_base {
* @param \MoodleQuickForm $mform
* @return object $mform
*/
public function setup_factor_form_definition($mform) {
public function setup_factor_form_definition(\MoodleQuickForm $mform): \MoodleQuickForm {
global $PAGE, $USER, $SESSION;
$mform->addElement('text', 'webauthn_name', get_string('authenticatorname', 'factor_webauthn'));
@@ -283,10 +289,10 @@ class factor extends object_factor_base {
/**
* WebAuthn Factor implementation.
*
* @param array $data
* @return array
* @param object $data
* @return stdClass|null
*/
public function setup_user_factor($data) {
public function setup_user_factor(object $data): stdClass|null {
global $DB, $USER, $SESSION;
if (!empty($data->webauthn_name) && !empty($data->response_input) && isset($SESSION->factor_webauthn_challenge)) {
@@ -32,10 +32,11 @@ $string['authenticator:usb'] = 'USB';
$string['authenticatorname'] = 'Security key name';
$string['authenticatortypelimitation'] = 'Please note that you can only use security keys of one of these types: {$a}.<br>Registering other security keys is possible, but you cannot use them during login.';
$string['error'] = 'Failed to authenticate';
$string['info'] = '<p>Use a WebAuthn supported authenticator</p>';
$string['loginskip'] = 'I don\'t have my authenticator';
$string['loginsubmit'] = 'Verify authenticator';
$string['pluginname'] = 'WebAuthn';
$string['info'] = '<p>Use a security key</p>';
$string['loginexplanation'] = 'Your account settings require that you authenticate with your security key in addition to your password.';
$string['loginskip'] = 'I don\'t have my security key';
$string['loginsubmit'] = 'Verify security key';
$string['pluginname'] = 'Security Key';
$string['privacy:metadata'] = 'The WebAuthn factor plugin does not store any personal data';
$string['register'] = 'Register authenticator';
$string['settings:authenticatortypes'] = 'Types of authenticator';
+4 -4
View File
@@ -25,9 +25,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2023052400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->release = 2023052400;
$plugin->requires = 2017051500.00; // Support back to 3.3 - Totara 12. Patches required.
$plugin->version = 2023080300; // The current plugin version (Date: YYYYMMDDXX).
$plugin->release = 2023062900;
$plugin->requires = 2023042400.00; // Supports from 4.2.
$plugin->component = 'factor_webauthn';
$plugin->maturity = MATURITY_ALPHA;
$plugin->dependencies = ['tool_mfa' => 2023031600];
$plugin->dependencies = ['tool_mfa' => 2023080300];
+1 -1
View File
@@ -37,7 +37,7 @@ if (!get_config('tool_mfa', 'guidance')) {
}
// Navigation. Target user preferences as previous node if authed.
if (isloggedin() && (!empty($SESSION->tool_mfa_authenticated) || $SESSION->tool_mfa_authenticated)) {
if (isloggedin() && (isset($SESSION->tool_mfa_authenticated) && $SESSION->tool_mfa_authenticated)) {
if ($node = $PAGE->settingsnav->find('usercurrentsettings', null)) {
$PAGE->navbar->add($node->get_content(), $node->action());
}
+1 -1
View File
@@ -142,7 +142,7 @@ $string['settings:guidancepage_help'] = 'HTML here will be displayed on the guid
$string['settings:lockout'] = 'Lockout threshold';
$string['settings:lockout_help'] = 'Amount of attempts a user has at answering input factors before they are not permitted to login.';
$string['settings:redir_exclusions'] = 'Urls which should not redirect the MFA check';
$string['settings:redir_exclusions_help'] = 'Each new line is a relative URL from the siteroot for which the MFA check will not redirect from eg. /admin/tool/securityquestions/set_responses.php';
$string['settings:redir_exclusions_help'] = 'Each new line is a relative URL from the siteroot for which the MFA check will not redirect from';
$string['settings:weight'] = 'Factor weight';
$string['settings:weight_help'] = 'The weight of this factor if passed. A user needs at least 100 points to login.';
$string['setup'] = 'Setup';
+13 -6
View File
@@ -23,6 +23,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core\context;
/**
* Main hook.
*
@@ -37,7 +39,7 @@
* @throws \moodle_exception
*/
function tool_mfa_after_require_login($courseorid = null, $autologinguest = null, $cm = null,
$setwantsurltome = null, $preventredirect = null) {
$setwantsurltome = null, $preventredirect = null): void {
global $SESSION;
// Tests for hooks being fired to test patches.
@@ -59,10 +61,10 @@ function tool_mfa_after_require_login($courseorid = null, $autologinguest = null
* @param stdClass $course
* @param context_course $coursecontext
*
* @return void or null
* @return mix void or null
* @throws \moodle_exception
*/
function tool_mfa_extend_navigation_user_settings($navigation, $user, $usercontext, $course, $coursecontext) {
function tool_mfa_extend_navigation_user_settings(navigation_node $navigation, stdClass $user, $usercontext, stdClass $course, $coursecontext) {
global $PAGE;
// Only inject if user is on the preferences page.
@@ -83,8 +85,10 @@ function tool_mfa_extend_navigation_user_settings($navigation, $user, $userconte
/**
* Triggered as soon as practical on every moodle bootstrap after config has
* been loaded. The $USER object is available at this point too.
*
* @return void
*/
function tool_mfa_after_config() {
function tool_mfa_after_config(): void {
global $CFG, $SESSION;
// Tests for hooks being fired to test patches.
@@ -104,8 +108,10 @@ function tool_mfa_after_config() {
/**
* Any plugin typically an admin tool can add new bulk user actions
*
* @return array
*/
function tool_mfa_bulk_user_actions() {
function tool_mfa_bulk_user_actions(): array {
return [
'tool_mfa_reset_factors' => new action_link(
new moodle_url('/admin/tool/mfa/reset_factor.php'),
@@ -126,7 +132,8 @@ function tool_mfa_bulk_user_actions() {
* @param array $options
* @return bool
*/
function tool_mfa_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = []) {
function tool_mfa_pluginfile(stdClass $course, stdClass $cm, context $context, string $filearea,
array $args, bool $forcedownload, array $options = []): bool {
// Hardcode to only send guidance files from the top level.
$fs = get_file_storage();
$file = $fs->get_file(
@@ -1,86 +0,0 @@
From 6d6f2d3543cd4b172aa85f0e47d7f531b7ec4d53 Mon Sep 17 00:00:00 2001
From: Brendan Heywood <[email protected]>
Date: Wed, 18 Oct 2017 16:20:33 +1100
Subject: [PATCH 1/2] MDL-60470 core: New hook 'after_require_login'
This adds a hook towards the end of the require_login function.
---
lib/moodlelib.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index 3ac3d8be1b7..d9e5baa175e 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -2705,6 +2705,8 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
$CFG->forceclean = true;
}
+ $afterlogins = get_plugins_with_function('after_require_login', 'lib.php');
+
// Do not bother admins with any formalities, except for activities pending deletion.
if (is_siteadmin() && !($cm && $cm->deletioninprogress)) {
// Set the global $COURSE.
@@ -2716,6 +2718,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
}
// Set accesstime or the user will appear offline which messes up messaging.
user_accesstime_log($course->id);
+
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
return;
}
@@ -2923,6 +2931,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
$PAGE->set_course($course);
}
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
+
// Finally access granted, update lastaccess times.
user_accesstime_log($course->id);
}
--
2.17.1
From 7235752ed449ab6662a317f059e444598bf1a862 Mon Sep 17 00:00:00 2001
From: Brendan Heywood <[email protected]>
Date: Thu, 8 Aug 2019 13:26:50 +1000
Subject: [PATCH 2/2] MDL-66340 setup: Add after_config for after setup.php is
loaded
---
lib/setup.php | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lib/setup.php b/lib/setup.php
index b1cb7e054ec..df1a2d978a5 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -1042,3 +1042,16 @@ if (false) {
$OUTPUT = new core_renderer(null, null);
$PAGE = new moodle_page();
}
+
+// Allow plugins to callback as soon possible after setup.php is loaded.
+$pluginswithfunction = get_plugins_with_function('after_config', 'lib.php');
+foreach ($pluginswithfunction as $plugins) {
+ foreach ($plugins as $function) {
+ try {
+ $function();
+ } catch (Exception $e) {
+ debugging("Exception calling '$function'", DEBUG_DEVELOPER, $e->getTrace());
+ }
+ }
+}
+
--
2.17.1
@@ -1,87 +0,0 @@
From 51484a595701e56897b0913974c566c6a13c1f32 Mon Sep 17 00:00:00 2001
From: Brendan Heywood <[email protected]>
Date: Wed, 18 Oct 2017 16:20:33 +1100
Subject: [PATCH 1/2] MDL-60470 core: New hook 'after_require_login'
This adds a hook towards the end of the require_login function.
---
lib/moodlelib.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index e2016a8bf75..29e11632233 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -2769,6 +2769,8 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
$CFG->forceclean = true;
}
+ $afterlogins = get_plugins_with_function('after_require_login', 'lib.php');
+
// Do not bother admins with any formalities, except for activities pending deletion.
if (is_siteadmin() && !($cm && $cm->deletioninprogress)) {
// Set the global $COURSE.
@@ -2783,6 +2785,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
if (!WS_SERVER && !AJAX_SCRIPT) {
user_accesstime_log($course->id);
}
+
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
return;
}
@@ -2990,6 +2998,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
$PAGE->set_course($course);
}
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
+
// Finally access granted, update lastaccess times.
// Do not update access time for webservice or ajax requests.
if (!WS_SERVER && !AJAX_SCRIPT) {
--
2.17.1
From 838ad593636395e4c4884e1bf47836504413b702 Mon Sep 17 00:00:00 2001
From: Brendan Heywood <[email protected]>
Date: Thu, 8 Aug 2019 13:26:50 +1000
Subject: [PATCH 2/2] MDL-66340 setup: Add after_config for after setup.php is
loaded
---
lib/setup.php | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lib/setup.php b/lib/setup.php
index 2cf12822c6d..a4e0acf0bb8 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -1041,3 +1041,16 @@ if (false) {
$OUTPUT = new core_renderer(null, null);
$PAGE = new moodle_page();
}
+
+// Allow plugins to callback as soon possible after setup.php is loaded.
+$pluginswithfunction = get_plugins_with_function('after_config', 'lib.php');
+foreach ($pluginswithfunction as $plugins) {
+ foreach ($plugins as $function) {
+ try {
+ $function();
+ } catch (Exception $e) {
+ debugging("Exception calling '$function'", DEBUG_DEVELOPER, $e->getTrace());
+ }
+ }
+}
+
--
2.17.1
@@ -1,34 +0,0 @@
From 1275e9b283c81b80b73d1c87c64449eb472cc037 Mon Sep 17 00:00:00 2001
From: Brendan Heywood <[email protected]>
Date: Thu, 8 Aug 2019 13:26:50 +1000
Subject: [PATCH] MDL-66340 setup: Add after_config for after setup.php is
loaded
---
lib/setup.php | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lib/setup.php b/lib/setup.php
index 2cf12822c6d..a4e0acf0bb8 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -1041,3 +1041,16 @@ if (false) {
$OUTPUT = new core_renderer(null, null);
$PAGE = new moodle_page();
}
+
+// Allow plugins to callback as soon possible after setup.php is loaded.
+$pluginswithfunction = get_plugins_with_function('after_config', 'lib.php');
+foreach ($pluginswithfunction as $plugins) {
+ foreach ($plugins as $function) {
+ try {
+ $function();
+ } catch (Exception $e) {
+ debugging("Exception calling '$function'", DEBUG_DEVELOPER, $e->getTrace());
+ }
+ }
+}
+
--
2.17.1
-61
View File
@@ -1,61 +0,0 @@
diff --git a/server/lib/moodlelib.php b/server/lib/moodlelib.php
index e80b84cb652..033de11df7f 100644
--- a/server/lib/moodlelib.php
+++ b/server/lib/moodlelib.php
@@ -2895,6 +2895,8 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
// Make sure the USER has a sesskey set up. Used for CSRF protection.
sesskey();
+ $afterlogins = get_plugins_with_function('after_require_login', 'lib.php');
+
// Do not bother admins with any formalities, except for activities pending deletion.
if (is_siteadmin() && !($cm && $cm->deletioninprogress)) {
// Set the global $COURSE.
@@ -2906,6 +2908,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
}
// Set accesstime or the user will appear offline which messes up messaging.
user_accesstime_log($course->id);
+
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
return;
}
@@ -3176,6 +3184,12 @@ function require_login($courseorid = null, $autologinguest = true, $cm = null, $
}
}
+ foreach ($afterlogins as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $pluginfunction($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
+ }
+ }
+
// Finally access granted, update lastaccess times.
user_accesstime_log($course->id);
}
diff --git a/server/lib/setup.php b/server/lib/setup.php
index c05e13b03aa..4d89eda0713 100644
--- a/server/lib/setup.php
+++ b/server/lib/setup.php
@@ -800,3 +800,17 @@ if (!function_exists('hash_equals')) {
return false;
}
}
+
+
+// Allow plugins to callback as soon possible after setup.php is loaded.
+$pluginswithfunction = get_plugins_with_function('after_config', 'lib.php');
+foreach ($pluginswithfunction as $plugins) {
+ foreach ($plugins as $function) {
+ try {
+ $function();
+ } catch (Throwable $e) {
+ debugging("Exception calling '$function'", DEBUG_DEVELOPER, $e->getTrace());
+ }
+ }
+}
+
+19 -18
View File
@@ -28,9 +28,9 @@ class tool_mfa_renderer extends plugin_renderer_base {
* Returns the state of the factor as a badge
*
* @param string $state
* @return html
* @return string
*/
public function get_state_badge($state) {
public function get_state_badge(string $state): string {
switch ($state) {
case \tool_mfa\plugininfo\factor::STATE_PASS:
@@ -57,9 +57,9 @@ class tool_mfa_renderer extends plugin_renderer_base {
/**
* Returns a list of factors which a user can add
*
* @return html
* @return string
*/
public function available_factors() {
public function available_factors(): string {
$html = $this->output->heading(get_string('preferences:availablefactors', 'tool_mfa'), 2);
$factors = \tool_mfa\plugininfo\factor::get_enabled_factors();
@@ -77,10 +77,10 @@ class tool_mfa_renderer extends plugin_renderer_base {
/**
* Returns the html section for factor setup
*
* @param object $factor object of the factor class
* @return void
* @param object $factor object of the factor class
* @return string
*/
public function setup_factor($factor) {
public function setup_factor(object $factor): string {
$html = '';
$html .= html_writer::start_tag('div', ['class' => 'card']);
@@ -105,7 +105,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
* @return string $html
* @throws \coding_exception
*/
public function active_factors() {
public function active_factors(): string {
global $USER, $CFG;
require_once($CFG->dirroot . '/iplookup/lib.php');
@@ -206,7 +206,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
*
* @return string $notification
*/
public function not_enough_factors() {
public function not_enough_factors(): string {
global $CFG, $SITE;
$notification = \html_writer::tag('h4', get_string('error:notenoughfactors', 'tool_mfa'));
@@ -231,7 +231,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
// Logout button.
$url = new \moodle_url('/admin/tool/mfa/auth.php', ['logout' => 1]);
$btn = new \single_button($url, get_string('logout'), 'post', true);
$btn = new \single_button($url, get_string('logout'), 'post', \single_button::BUTTON_PRIMARY);
$return .= $this->render($btn);
$return .= $this->guide_link();
@@ -245,7 +245,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
* @param int $lookback the period to view.
* @return string the HTML for the table
*/
public function factors_in_use_table($lookback) {
public function factors_in_use_table(int $lookback): string {
global $DB;
$factors = \tool_mfa\plugininfo\factor::get_factors();
@@ -326,8 +326,8 @@ class tool_mfa_renderer extends plugin_renderer_base {
// Auth rows.
$authtypes = get_enabled_auth_plugins(true);
$row = [];
foreach ($authtypes as $authtype) {
$row = [];
$row[] = \html_writer::tag('b', $authtype);
// Setup the overall totals columns.
@@ -380,7 +380,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
*
* @return string the HTML for the table
*/
public function factors_locked_table() {
public function factors_locked_table(): string {
global $DB;
$factors = \tool_mfa\plugininfo\factor::get_factors();
@@ -435,7 +435,7 @@ class tool_mfa_renderer extends plugin_renderer_base {
* @param object $factor the factor class
* @return string the HTML for the table
*/
public function factor_locked_users_table($factor) {
public function factor_locked_users_table(object $factor): string {
global $DB;
$table = new html_table();
@@ -509,9 +509,9 @@ class tool_mfa_renderer extends plugin_renderer_base {
/**
* Returns a html section render of the guide link template
*
* @return string
* @return string
*/
public function guide_link() {
public function guide_link(): string {
if (!get_config('tool_mfa', 'guidance')) {
return '';
}
@@ -528,11 +528,12 @@ class tool_mfa_renderer extends plugin_renderer_base {
* @param HTML_QuickForm_element $element element
* @param bool $required if input is required field
* @param bool $advanced if input is an advanced field
* @param string $error error message to display
* @param string|null $error error message to display
* @param bool $ingroup True if this element is rendered as part of a group
* @return mixed string|bool
*/
public function mform_element($element, $required, $advanced, $error, $ingroup) {
public function mform_element(HTML_QuickForm_element $element, bool $required,
bool $advanced, string|null $error, bool $ingroup): string|bool {
$script = null;
if ($element instanceof tool_mfa\local\form\verification_field) {
if ($this->page->pagelayout === 'secure') {
+3 -9
View File
@@ -73,15 +73,9 @@ if ($hassiteconfig) {
$plugin->load_settings($ADMIN, 'toolmfafolder', $hassiteconfig);
}
if (file_exists($CFG->dirroot . '/totara')) {
// Totara navigation.
$section = 'toolmfafolder';
} else {
// Moodle navigation.
$ADMIN->add('reports', new admin_category('toolmfareports', get_string('mfareports', 'tool_mfa')));
$section = 'toolmfareports';
}
$ADMIN->add($section,
$ADMIN->add('reports', new admin_category('toolmfareports', get_string('mfareports', 'tool_mfa')));
$ADMIN->add('toolmfareports',
new admin_externalpage('factorreport', get_string('factorreport', 'tool_mfa'),
new moodle_url('/admin/tool/mfa/factor_report.php')));
}

Some files were not shown because too many files have changed in this diff Show More