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:
-15
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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"}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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') {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user