├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitmodules ├── README.md ├── action.php ├── amd ├── build │ ├── autosubmit_verification_code.min.js │ └── autosubmit_verification_code.min.js.map └── src │ └── autosubmit_verification_code.js ├── auth.php ├── classes ├── event │ ├── user_deleted_factor.php │ ├── user_failed_mfa.php │ ├── user_passed_mfa.php │ ├── user_revoked_factor.php │ └── user_setup_factor.php ├── local │ ├── admin_setting_managemfa.php │ ├── factor │ │ ├── fallback.php │ │ ├── object_factor.php │ │ └── object_factor_base.php │ ├── form │ │ ├── global_form_manager.php │ │ ├── login_form.php │ │ ├── reset_factor.php │ │ ├── revoke_factor_form.php │ │ ├── setup_factor_form.php │ │ └── verification_field.php │ └── secret_manager.php ├── manager.php ├── plugininfo │ └── factor.php └── privacy │ └── provider.php ├── db ├── access.php ├── install.xml ├── subplugins.json ├── subplugins.php └── upgrade.php ├── factor ├── admin │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_admin.php │ ├── settings.php │ └── version.php ├── auth │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── db │ │ └── upgrade.php │ ├── lang │ │ └── en │ │ │ └── factor_auth.php │ ├── settings.php │ └── version.php ├── capability │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── db │ │ └── access.php │ ├── lang │ │ └── en │ │ │ └── factor_capability.php │ ├── settings.php │ └── version.php ├── cohort │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_cohort.php │ ├── settings.php │ └── version.php ├── email │ ├── classes │ │ ├── event │ │ │ └── unauth_email.php │ │ ├── factor.php │ │ ├── form │ │ │ └── email.php │ │ └── privacy │ │ │ └── provider.php │ ├── email.php │ ├── lang │ │ └── en │ │ │ └── factor_email.php │ ├── renderer.php │ ├── settings.php │ ├── templates │ │ └── email.mustache │ ├── tests │ │ └── factor_test.php │ └── version.php ├── grace │ ├── classes │ │ ├── factor.php │ │ ├── privacy │ │ │ └── provider.php │ │ └── task │ │ │ └── revoke_expired_factors.php │ ├── db │ │ └── tasks.php │ ├── lang │ │ └── en │ │ │ └── factor_grace.php │ ├── settings.php │ ├── tests │ │ └── factor_test.php │ └── version.php ├── iprange │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_iprange.php │ ├── settings.php │ └── version.php ├── loginbanner │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_loginbanner.php │ ├── settings.php │ └── version.php ├── nosetup │ ├── classes │ │ ├── factor.php │ │ ├── privacy │ │ │ └── provider.php │ │ └── task │ │ │ └── delete_unusable_factors.php │ ├── db │ │ └── tasks.php │ ├── lang │ │ └── en │ │ │ └── factor_nosetup.php │ ├── settings.php │ └── version.php ├── role │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_role.php │ ├── settings.php │ └── version.php ├── secq │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_secq.php │ ├── settings.php │ └── version.php ├── sms │ ├── classes │ │ ├── event │ │ │ └── sms_sent.php │ │ ├── factor.php │ │ ├── helper.php │ │ ├── local │ │ │ └── smsgateway │ │ │ │ ├── aws_sns.php │ │ │ │ ├── gateway_interface.php │ │ │ │ └── modica.php │ │ └── privacy │ │ │ └── provider.php │ ├── db │ │ └── upgrade.php │ ├── lang │ │ └── en │ │ │ └── factor_sms.php │ ├── settings.php │ └── version.php ├── token │ ├── classes │ │ ├── event │ │ │ └── token_created.php │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── lang │ │ └── en │ │ │ └── factor_token.php │ ├── settings.php │ ├── tests │ │ └── factor_test.php │ └── version.php ├── totp │ ├── classes │ │ ├── factor.php │ │ └── privacy │ │ │ └── provider.php │ ├── extlib │ │ ├── Assert │ │ │ ├── Assertion.php │ │ │ ├── AssertionFailedException.php │ │ │ └── InvalidArgumentException.php │ │ ├── OTPHP │ │ │ ├── OTP.php │ │ │ ├── OTPInterface.php │ │ │ ├── ParameterTrait.php │ │ │ ├── TOTP.php │ │ │ └── TOTPInterface.php │ │ └── ParagonIE │ │ │ └── ConstantTime │ │ │ ├── Base32.php │ │ │ ├── Binary.php │ │ │ └── EncoderInterface.php │ ├── lang │ │ └── en │ │ │ └── factor_totp.php │ ├── settings.php │ ├── tests │ │ └── factor_test.php │ ├── thirdpartylibs.xml │ └── version.php └── webauthn │ ├── .extlib │ └── WebAuthn │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── _test │ │ ├── client.html │ │ ├── rootCertificates │ │ │ ├── apple.pem │ │ │ ├── globalSign.pem │ │ │ ├── googleHardware.pem │ │ │ ├── hypersecu.pem │ │ │ ├── mds │ │ │ │ └── .gitkeep │ │ │ ├── microsoftTpmCollection.pem │ │ │ ├── solo.pem │ │ │ └── yubico.pem │ │ └── server.php │ │ ├── composer.json │ │ └── src │ │ ├── Attestation │ │ ├── AttestationObject.php │ │ ├── AuthenticatorData.php │ │ └── Format │ │ │ ├── AndroidKey.php │ │ │ ├── AndroidSafetyNet.php │ │ │ ├── Apple.php │ │ │ ├── FormatBase.php │ │ │ ├── None.php │ │ │ ├── Packed.php │ │ │ ├── Tpm.php │ │ │ └── U2f.php │ │ ├── Binary │ │ └── ByteBuffer.php │ │ ├── CBOR │ │ └── CborDecoder.php │ │ ├── WebAuthn.php │ │ └── WebAuthnException.php │ ├── amd │ ├── build │ │ ├── login.min.js │ │ ├── login.min.js.map │ │ ├── register.min.js │ │ ├── register.min.js.map │ │ ├── utils.min.js │ │ └── utils.min.js.map │ └── src │ │ ├── login.js │ │ ├── register.js │ │ └── utils.js │ ├── classes │ ├── factor.php │ └── privacy │ │ └── provider.php │ ├── lang │ └── en │ │ └── factor_webauthn.php │ ├── settings.php │ ├── thirdpartylibs.xml │ └── version.php ├── factor_report.php ├── guide.php ├── index.php ├── lang └── en │ └── tool_mfa.php ├── lib.php ├── phpunit.xml ├── renderer.php ├── reset_factor.php ├── settings.php ├── styles.css ├── templates └── guide_link.mustache ├── tests ├── admin_setting_managemfa_test.php ├── manager_test.php ├── object_factor_base_test.php ├── plugininfo_factor_test.php ├── secret_manager_test.php └── tool_mfa_testcase.php ├── thirdpartylibs.xml ├── user_preferences.php └── version.php /.gitattributes: -------------------------------------------------------------------------------- 1 | **/amd/build/** -diff 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/ci.yml 2 | name: ci 3 | 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | test: 8 | uses: catalyst/catalyst-moodle-workflows/.github/workflows/ci.yml@main 9 | secrets: 10 | # Required if you plan to publish (uncomment the below) 11 | moodle_org_token: ${{ secrets.MOODLE_ORG_TOKEN }} 12 | with: 13 | #Grunt fails due to CSS styling needing an !important. 14 | disable_grunt: true 15 | disable_phpcpd: true 16 | release_branches: MOODLE_400_STABLE 17 | disable_master: true 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "factor/exemption"] 2 | path = factor/exemption 3 | url = https://github.com/catalyst/moodle-factor_exemption.git 4 | -------------------------------------------------------------------------------- /amd/build/autosubmit_verification_code.min.js: -------------------------------------------------------------------------------- 1 | define ("tool_mfa/autosubmit_verification_code",[],function(){return{init:function init(){document.querySelector("#id_verificationcode").addEventListener("keyup",function(){if(6==this.value.length){this.closest("form").submit()}})}}}); 2 | //# sourceMappingURL=autosubmit_verification_code.min.js.map 3 | -------------------------------------------------------------------------------- /amd/build/autosubmit_verification_code.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/autosubmit_verification_code.js"],"names":["define","init","document","querySelector","addEventListener","value","length","closest","submit"],"mappings":"AAwBAA,OAAM,yCAAC,EAAD,CAAK,UAAW,CAClB,MAAO,CACHC,IAAI,CAAE,eAAW,CACbC,QAAQ,CAACC,aAAT,CAAuB,sBAAvB,EAA+CC,gBAA/C,CAAgE,OAAhE,CAAyE,UAAW,CAChF,GAAyB,CAArB,OAAKC,KAAL,CAAWC,MAAf,CAA4B,CAExB,KAAKC,OAAL,CAAa,MAAb,EAAqBC,MAArB,EACH,CACJ,CALD,CAMH,CARE,CAUV,CAXK,CAAN","sourcesContent":["\n// 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 .\n\n/**\n * Module to autosubmit the verification code element when it reaches 6 characters.\n *\n * @module tool_mfa/autosubmit_verification_code\n * @copyright 2020 Peter Burnett \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([], function() {\n return {\n init: function() {\n document.querySelector(\"#id_verificationcode\").addEventListener('keyup', function() {\n if (this.value.length == 6) {\n // Submits the closes form (parent).\n this.closest(\"form\").submit();\n }\n });\n }\n };\n});\n"],"file":"autosubmit_verification_code.min.js"} -------------------------------------------------------------------------------- /amd/src/autosubmit_verification_code.js: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of Moodle - http://moodle.org/ 3 | // 4 | // Moodle is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // Moodle is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with Moodle. If not, see . 16 | 17 | /** 18 | * Module to autosubmit the verification code element when it reaches 6 characters. 19 | * 20 | * @module tool_mfa/autosubmit_verification_code 21 | * @copyright 2020 Peter Burnett 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | define([], function() { 26 | return { 27 | init: function() { 28 | document.querySelector("#id_verificationcode").addEventListener('keyup', function() { 29 | if (this.value.length == 6) { 30 | // Submits the closes form (parent). 31 | this.closest("form").submit(); 32 | } 33 | }); 34 | } 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /classes/event/user_passed_mfa.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\event; 18 | 19 | /** 20 | * Event for when user successfully passed all MFA factor checks. 21 | * 22 | * @property-read array $other { 23 | * Extra information about event. 24 | * } 25 | * 26 | * @package tool_mfa 27 | * @author Mikhail Golenkov 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class user_passed_mfa extends \core\event\base { 32 | 33 | /** 34 | * Create instance of event. 35 | * 36 | * @param int $user the User object of the User who passed all MFA factor checks. 37 | * 38 | * @return user_passed_mfa the user_passed_mfa event 39 | * 40 | * @throws \coding_exception 41 | */ 42 | public static function user_passed_mfa_event($user) { 43 | 44 | // Build debug info string. 45 | $factors = \tool_mfa\plugininfo\factor::get_active_user_factor_types(); 46 | $debug = ''; 47 | foreach ($factors as $factor) { 48 | $debug .= "
Factor {$factor->name} status: {$factor->get_state()}"; 49 | } 50 | 51 | $data = [ 52 | 'relateduserid' => null, 53 | 'context' => \context_user::instance($user->id), 54 | 'other' => [ 55 | 'userid' => $user->id, 56 | 'debug' => $debug, 57 | ], 58 | ]; 59 | 60 | return self::create($data); 61 | } 62 | 63 | /** 64 | * Init method. 65 | * 66 | * @return void 67 | */ 68 | protected function init() { 69 | $this->data['crud'] = 'r'; 70 | $this->data['edulevel'] = self::LEVEL_OTHER; 71 | } 72 | 73 | /** 74 | * Returns description of what happened. 75 | * 76 | * @return string 77 | */ 78 | public function get_description() { 79 | return "The user with id '{$this->other['userid']}' successfully passed MFA.
Information: {$this->other['debug']}"; 80 | } 81 | 82 | /** 83 | * Return localised event name. 84 | * 85 | * @return string 86 | * @throws \coding_exception 87 | */ 88 | public static function get_name() { 89 | return get_string('event:userpassedmfa', 'tool_mfa'); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /classes/event/user_revoked_factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\event; 18 | 19 | /** 20 | * Event for when user successfully revoked MFA Factor. 21 | * 22 | * @property-read array $other { 23 | * Extra information about event. 24 | * } 25 | * 26 | * @package tool_mfa 27 | * @author Mikhail Golenkov 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class user_revoked_factor extends \core\event\base { 32 | 33 | /** 34 | * Create instance of event. 35 | * 36 | * @param int $user the User object of the User who has revoked new factor 37 | * @param string $factorname revoked factor 38 | * 39 | * @return user_passed_mfa the user_passed_mfa event 40 | * 41 | * @throws \coding_exception 42 | */ 43 | public static function user_revoked_factor_event($user, $factorname) { 44 | 45 | $data = [ 46 | 'relateduserid' => null, 47 | 'context' => \context_user::instance($user->id), 48 | 'other' => [ 49 | 'userid' => $user->id, 50 | 'factorname' => $factorname, 51 | ], 52 | ]; 53 | 54 | return self::create($data); 55 | } 56 | 57 | /** 58 | * Init method. 59 | * 60 | * @return void 61 | */ 62 | protected function init() { 63 | $this->data['crud'] = 'd'; 64 | $this->data['edulevel'] = self::LEVEL_OTHER; 65 | } 66 | 67 | /** 68 | * Returns description of what happened. 69 | * 70 | * @return string 71 | */ 72 | public function get_description() { 73 | return "The user with id '{$this->other['userid']}' successfully revoked {$this->other['factorname']}"; 74 | } 75 | 76 | /** 77 | * Return localised event name. 78 | * 79 | * @return string 80 | * @throws \coding_exception 81 | */ 82 | public static function get_name() { 83 | return get_string('event:userrevokedfactor', 'tool_mfa'); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /classes/event/user_setup_factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\event; 18 | 19 | /** 20 | * Event for when user successfully setup new MFA Factor. 21 | * 22 | * @property-read array $other { 23 | * Extra information about event. 24 | * } 25 | * 26 | * @package tool_mfa 27 | * @author Mikhail Golenkov 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class user_setup_factor extends \core\event\base { 32 | 33 | /** 34 | * Create instance of event. 35 | * 36 | * @param object $user the User object of the User who has setup new factor 37 | * @param string $factorname setup factor 38 | * 39 | * @return user_passed_mfa the user_passed_mfa event 40 | * 41 | * @throws \coding_exception 42 | */ 43 | public static function user_setup_factor_event($user, $factorname) { 44 | 45 | $data = [ 46 | 'relateduserid' => null, 47 | 'context' => \context_user::instance($user->id), 48 | 'other' => [ 49 | 'userid' => $user->id, 50 | 'factorname' => $factorname, 51 | ], 52 | ]; 53 | 54 | return self::create($data); 55 | } 56 | 57 | /** 58 | * Init method. 59 | * 60 | * @return void 61 | */ 62 | protected function init() { 63 | $this->data['crud'] = 'c'; 64 | $this->data['edulevel'] = self::LEVEL_OTHER; 65 | } 66 | 67 | /** 68 | * Returns description of what happened. 69 | * 70 | * @return string 71 | */ 72 | public function get_description() { 73 | return "The user with id '{$this->other['userid']}' successfully setup {$this->other['factorname']}"; 74 | } 75 | 76 | /** 77 | * Return localised event name. 78 | * 79 | * @return string 80 | * @throws \coding_exception 81 | */ 82 | public static function get_name() { 83 | return get_string('event:usersetupfactor', 'tool_mfa'); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /classes/local/factor/fallback.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\local\factor; 18 | 19 | /** 20 | * Fallback factor class. 21 | * 22 | * @package tool_mfa 23 | * @author Peter Burnett 24 | * @copyright Catalyst IT 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class fallback extends object_factor_base { 28 | 29 | /** 30 | * Overridden constructor. Name is hard set to 'fallback'. 31 | */ 32 | public function __construct() { 33 | $this->name = 'fallback'; 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public function get_display_name() { 40 | return get_string('fallback', 'tool_mfa'); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | public function get_info() { 47 | return get_string('fallback_info', 'tool_mfa'); 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function get_state() { 54 | return \tool_mfa\plugininfo\factor::STATE_FAIL; 55 | } 56 | 57 | /** 58 | * Sets the state of the factor check into the session. 59 | * Returns whether storing the var was successful. 60 | * 61 | * @param mixed $state 62 | * @return bool 63 | */ 64 | public function set_state($state) { 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /classes/local/form/global_form_manager.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\local\form; 18 | 19 | use tool_mfa\plugininfo\factor; 20 | 21 | /** 22 | * MFA login form 23 | * 24 | * @package tool_mfa 25 | * @author Mikhail Golenkov 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class global_form_manager { 30 | /** @var array factors to call hooks upon. */ 31 | private $activefactors; 32 | 33 | /** 34 | * Create an instance of this class. 35 | */ 36 | public function __construct() { 37 | $this->activefactors = factor::get_active_user_factor_types(); 38 | } 39 | 40 | /** 41 | * Hook point for global auth form action hooks. 42 | * 43 | * @param \MoodleQuickForm $mform Form to inject global elements into. 44 | * @return void 45 | */ 46 | public function definition(&$mform) { 47 | foreach ($this->activefactors as $factor) { 48 | $factor->global_definition($mform); 49 | } 50 | } 51 | 52 | /** 53 | * Hook point for global auth form action hooks. 54 | * 55 | * @param \MoodleQuickForm $mform Form to inject global elements into. 56 | * @return void 57 | */ 58 | public function definition_after_data(&$mform) { 59 | foreach ($this->activefactors as $factor) { 60 | $factor->global_definition_after_data($mform); 61 | } 62 | } 63 | 64 | /** 65 | * Hook point for global auth form action hooks. 66 | * 67 | * @param array $data Data from the form. 68 | * @param array $files Files form the form. 69 | * @return array of errors from validation. 70 | */ 71 | public function validation($data, $files) { 72 | $errors = []; 73 | foreach ($this->activefactors as $factor) { 74 | $errors = array_merge($errors, $factor->global_validation($data, $files)); 75 | } 76 | return $errors; 77 | } 78 | 79 | /** 80 | * Hook point for global auth form submission hooks. 81 | * 82 | * @param \stdClass $data Data from the form. 83 | * @return void 84 | */ 85 | public function submit(\stdClass $data) { 86 | foreach ($this->activefactors as $factor) { 87 | $factor->global_submit($data); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /classes/local/form/revoke_factor_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\local\form; 18 | 19 | defined('MOODLE_INTERNAL') || die(); 20 | 21 | require_once($CFG->libdir . "/formslib.php"); 22 | 23 | /** 24 | * Revoke factor form 25 | * 26 | * @package tool_mfa 27 | * @author Mikhail Golenkov 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class revoke_factor_form extends \moodleform { 32 | 33 | /** 34 | * {@inheritDoc} 35 | * @see moodleform::definition() 36 | */ 37 | public function definition() { 38 | global $OUTPUT; 39 | $mform = $this->_form; 40 | $factorname = $this->_customdata['factorname']; 41 | $devicename = $this->_customdata['devicename']; 42 | 43 | $mform->addElement('html', $OUTPUT->heading(get_string('areyousure', 'tool_mfa'), 4)); 44 | $mform->addElement('html', $OUTPUT->heading(get_string('factor', 'tool_mfa').': '.$factorname, 5)); 45 | $mform->addElement('html', $OUTPUT->heading(get_string('devicename', 'tool_mfa').': '.$devicename, 5)); 46 | 47 | $this->add_action_buttons(true, get_string('revoke', 'tool_mfa')); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /classes/local/form/setup_factor_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\local\form; 18 | 19 | defined('MOODLE_INTERNAL') || die(); 20 | 21 | require_once($CFG->libdir . "/formslib.php"); 22 | 23 | /** 24 | * Setup factor form 25 | * 26 | * @package tool_mfa 27 | * @author Mikhail Golenkov 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class setup_factor_form extends \moodleform { 32 | 33 | /** 34 | * {@inheritDoc} 35 | * @see moodleform::definition() 36 | */ 37 | public function definition() { 38 | $mform = $this->_form; 39 | 40 | $factorname = $this->_customdata['factorname']; 41 | $factor = \tool_mfa\plugininfo\factor::get_factor($factorname); 42 | $mform = $factor->setup_factor_form_definition($mform); 43 | 44 | } 45 | 46 | /** 47 | * Validates setup_factor form with given factor validation method. 48 | * 49 | * @param array $data 50 | * @param array $files 51 | * @return array 52 | */ 53 | public function validation($data, $files) { 54 | $errors = parent::validation($data, $files); 55 | 56 | $factorname = $this->_customdata['factorname']; 57 | $factor = \tool_mfa\plugininfo\factor::get_factor($factorname); 58 | $errors += $factor->setup_factor_form_validation($data); 59 | 60 | return $errors; 61 | } 62 | 63 | /** 64 | * Invokes factor setup_factor_form_definition_after_data() method after form data has been set. 65 | */ 66 | public function definition_after_data() { 67 | $mform = $this->_form; 68 | 69 | $factorname = $this->_customdata['factorname']; 70 | $factor = \tool_mfa\plugininfo\factor::get_factor($factorname); 71 | $mform = $factor->setup_factor_form_definition_after_data($mform); 72 | $this->add_action_buttons(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Definition of MFA sub-plugins (factors). 19 | * 20 | * @package tool_mfa 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | defined('MOODLE_INTERNAL') || die; 26 | 27 | $capabilities = [ 28 | 'tool/mfa:mfaaccess' => [ 29 | 'captype' => 'write', 30 | 'contextlevel' => CONTEXT_USER, 31 | 'archetypes' => ['user' => CAP_ALLOW], 32 | ], 33 | ]; 34 | -------------------------------------------------------------------------------- /db/subplugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugintypes": { 3 | "factor": "admin\/tool\/mfa\/factor" 4 | } 5 | } -------------------------------------------------------------------------------- /db/subplugins.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Definition of MFA sub-plugins (factors). 19 | * 20 | * @package tool_mfa 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $subplugins = (array) json_decode(file_get_contents($CFG->dirroot."/admin/tool/mfa/db/subplugins.json"))->plugintypes; 29 | -------------------------------------------------------------------------------- /factor/admin/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_admin; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * Admin factor class. 23 | * 24 | * @package factor_admin 25 | * @author Peter Burnett 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * Admin Factor implementation. 33 | * Factor is a singleton, can only be one instance. 34 | * 35 | * @param stdClass $user the user to check against. 36 | * @return array 37 | */ 38 | public function get_all_user_factors($user) { 39 | global $DB; 40 | $records = $DB->get_records('tool_mfa', ['userid' => $user->id, 'factor' => $this->name]); 41 | 42 | if (!empty($records)) { 43 | return $records; 44 | } 45 | 46 | // Null records returned, build new record. 47 | $record = [ 48 | 'userid' => $user->id, 49 | 'factor' => $this->name, 50 | 'timecreated' => time(), 51 | 'createdfromip' => $user->lastip, 52 | 'timemodified' => time(), 53 | 'revoked' => 0, 54 | ]; 55 | $record['id'] = $DB->insert_record('tool_mfa', $record, true); 56 | return [(object) $record]; 57 | } 58 | 59 | /** 60 | * Admin Factor implementation. 61 | * Factor does not have input. 62 | * 63 | * {@inheritDoc} 64 | */ 65 | public function has_input() { 66 | return false; 67 | } 68 | 69 | /** 70 | * Admin Factor implementation. 71 | * State check is performed here, as there is no form to do it in. 72 | * 73 | * {@inheritDoc} 74 | */ 75 | public function get_state() { 76 | if (is_siteadmin()) { 77 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 78 | } 79 | return \tool_mfa\plugininfo\factor::STATE_PASS; 80 | } 81 | 82 | /** 83 | * Admin Factor implementation. 84 | * The state can never be set. Always return true. 85 | * 86 | * @param mixed $state the state constant to set 87 | * @return bool 88 | */ 89 | public function set_state($state) { 90 | return true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /factor/admin/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_admin\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_admin 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/admin/lang/en/factor_admin.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_admin 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['info'] = 'This factor allows for NOT being an administrator to count as a factor. Its intended use is to ensure administators require tighter security, so regular users get the weight for free, while admins must use other factors.'; 27 | $string['pluginname'] = 'Non-administrator'; 28 | $string['privacy:metadata'] = 'The Admin factor plugin does not store any personal data'; 29 | $string['settings:weight_help'] = 'Weight is given to regular users for this factor, so admins must have more factors than a regular user to pass.'; 30 | $string['summarycondition'] = 'is not an admin'; 31 | -------------------------------------------------------------------------------- /factor/admin/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Admin factor Settings. 19 | * 20 | * @package factor_admin 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_admin/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('admin', get_config('factor_admin', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_admin/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'factor_admin'), 100, PARAM_INT)); 39 | -------------------------------------------------------------------------------- /factor/admin/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_admin 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_admin'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/auth/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_auth; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * Auth factor class. 23 | * 24 | * @package factor_auth 25 | * @author Mikhail Golenkov 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * Auth Factor implementation. 33 | * Factor is a singleton, can only be one instance. 34 | * 35 | * @param stdClass $user the user to check against. 36 | * @return array 37 | */ 38 | public function get_all_user_factors($user) { 39 | return $this->get_singleton_user_factor($user); 40 | } 41 | 42 | /** 43 | * Auth Factor implementation. 44 | * Factor does not have input. 45 | * 46 | * {@inheritDoc} 47 | */ 48 | public function has_input() { 49 | return false; 50 | } 51 | 52 | /** 53 | * Auth Factor implementation. 54 | * State check is performed here, as there is no form to do it in. 55 | * 56 | * {@inheritDoc} 57 | */ 58 | public function get_state() { 59 | global $USER; 60 | 61 | $safetypes = get_config('factor_auth', 'goodauth'); 62 | if (strlen($safetypes) != 0) { 63 | $safetypes = explode(',', $safetypes); 64 | 65 | // Check all safetypes against user auth. 66 | if (in_array($USER->auth, $safetypes, true)) { 67 | return \tool_mfa\plugininfo\factor::STATE_PASS; 68 | } 69 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 70 | } else { 71 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 72 | } 73 | } 74 | 75 | /** 76 | * Auth Factor implementation. 77 | * The state can never be set. Always return true. 78 | * 79 | * @param mixed $state the state constant to set 80 | * @return bool 81 | */ 82 | public function set_state($state) { 83 | return true; 84 | } 85 | 86 | /** 87 | * Auth factor implementation. 88 | * Return list of auth types that are safe. 89 | * 90 | * {@inheritDoc} 91 | */ 92 | public function get_summary_condition() { 93 | $safetypes = get_config('factor_auth', 'goodauth'); 94 | 95 | return get_string('summarycondition', 'factor_'.$this->name, $safetypes); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /factor/auth/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_auth\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_auth 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/auth/db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * factor_auth upgrade library. 19 | * 20 | * @package factor_auth 21 | * @copyright 2021 Peter Burnett 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * Factor auth upgrade helper function 27 | * 28 | * @param int $oldversion 29 | */ 30 | function xmldb_factor_auth_upgrade($oldversion) { 31 | 32 | if ($oldversion < 2021020500) { 33 | $authtypes = get_enabled_auth_plugins(true); 34 | // Upgrade goodauth config from number to name. 35 | $goodauth = explode(',', get_config('factor_auth', 'goodauth')); 36 | $newauths = []; 37 | foreach ($goodauth as $auth) { 38 | // Check if index exists before access. If not, ignore, settings were out of sync. 39 | if (array_key_exists($auth, $authtypes)) { 40 | $newauths[] = $authtypes[$auth]; 41 | } 42 | } 43 | set_config('goodauth', implode(',', $newauths), 'factor_auth'); 44 | 45 | // MFA savepoint reached. 46 | upgrade_plugin_savepoint(true, 2021020500, 'factor', 'auth'); 47 | } 48 | 49 | return true; 50 | } 51 | -------------------------------------------------------------------------------- /factor/auth/lang/en/factor_auth.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_auth 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['info'] = 'Check the type of authentication used to login as an MFA factor.'; 27 | $string['pluginname'] = 'Authentication type'; 28 | $string['privacy:metadata'] = 'The Auth Factor plugin does not store any personal data'; 29 | $string['settings:goodauth'] = 'Factor authentication types'; 30 | $string['settings:goodauth_help'] = 'Select all authentication types to use as a factor for MFA. Any types not selected will not be treated as a FAIL in MFA.'; 31 | $string['summarycondition'] = 'has an authentication type of {$a}'; 32 | -------------------------------------------------------------------------------- /factor/auth/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_auth 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_auth/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('auth', get_config('factor_auth', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_auth/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | 40 | $authtypes = get_enabled_auth_plugins(true); 41 | $authselect = []; 42 | foreach ($authtypes as $type) { 43 | $auth = get_auth_plugin($type); 44 | $authselect[$type] = $auth->get_title(); 45 | } 46 | 47 | $settings->add(new admin_setting_configmulticheckbox('factor_auth/goodauth', 48 | get_string('settings:goodauth', 'factor_auth'), 49 | get_string('settings:goodauth_help', 'factor_auth'), [], $authselect)); 50 | -------------------------------------------------------------------------------- /factor/auth/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_auth 21 | * @subpackage tool_mfa 22 | * @author Mikhail Golenkov 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2021020500; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_auth'; 32 | $plugin->release = 2021020500; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/capability/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_capability; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * User capability factor class. 23 | * 24 | * @package factor_capability 25 | * @author Peter Burnett 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * User capability implementation. 33 | * This factor is a singleton, return single instance. 34 | * 35 | * @param stdClass $user the user to check against. 36 | * @return array 37 | */ 38 | public function get_all_user_factors($user) { 39 | return $this->get_singleton_user_factor($user); 40 | } 41 | 42 | /** 43 | * User capability implementation. 44 | * Factor has no input 45 | * 46 | * {@inheritDoc} 47 | */ 48 | public function has_input() { 49 | return false; 50 | } 51 | 52 | /** 53 | * User capability implementation. 54 | * Checks whether user has the negative capability. 55 | * 56 | * {@inheritDoc} 57 | */ 58 | public function get_state() { 59 | global $USER; 60 | $adminpass = (bool) get_config('factor_capability', 'adminpasses'); 61 | 62 | // Do anything check is controlled from factor config. 63 | if (!has_capability('factor/capability:cannotpassfactor', \context_system::instance(), $USER, $adminpass)) { 64 | return \tool_mfa\plugininfo\factor::STATE_PASS; 65 | } else { 66 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 67 | } 68 | } 69 | 70 | /** 71 | * User Capability implementation. 72 | * Cannot set state, return true. 73 | * 74 | * @param mixed $state the state constant to set 75 | * @return bool 76 | */ 77 | public function set_state($state) { 78 | return true; 79 | } 80 | 81 | /** 82 | * User capability implementation. 83 | * Possible states are either neutral or pass. 84 | * 85 | * @param \stdClass $user 86 | */ 87 | public function possible_states($user) { 88 | return [ 89 | \tool_mfa\plugininfo\factor::STATE_PASS, 90 | \tool_mfa\plugininfo\factor::STATE_NEUTRAL, 91 | ]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /factor/capability/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_capability\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_capability 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/capability/db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * User capability factor access declaration. 19 | * 20 | * @package factor_capability 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $capabilities = [ 29 | 'factor/capability:cannotpassfactor' => [ 30 | 'captype' => 'read', 31 | 'contextlevel' => CONTEXT_SYSTEM, 32 | ], 33 | ]; 34 | -------------------------------------------------------------------------------- /factor/capability/lang/en/factor_capability.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_capability 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['capability:cannotpassfactor'] = 'STOPS a role from passing the MFA user capability factor.'; 27 | $string['pluginname'] = 'User capability'; 28 | $string['privacy:metadata'] = 'The user capability factor plugin does not store any personal data'; 29 | $string['settings:adminpasses'] = 'Site admins can pass this factor'; 30 | $string['settings:adminpasses_help'] = 'By default admins pass all capability checks, including this one which uses \'factor/capability:cannotpassfactor\', which means they will fail this factor. 31 | If checked then all site admins will pass this factor if they do not have this capability from another role. 32 | If unchecked site admins will fail this factor.'; 33 | $string['summarycondition'] = 'does NOT have the factor/capability:cannotpassfactor capability in any role including site administrator.'; 34 | -------------------------------------------------------------------------------- /factor/capability/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_capability 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_capability/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('capability', get_config('factor_capability', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_capability/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | 40 | // Admin passes bool logic is inverted due to negative capability check. 41 | $settings->add(new admin_setting_configcheckbox('factor_capability/adminpasses', 42 | new lang_string('settings:adminpasses', 'factor_capability'), 43 | new lang_string('settings:adminpasses_help', 'factor_capability'), 1, 0, 1)); 44 | -------------------------------------------------------------------------------- /factor/capability/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_capability 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2020071400; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_capability'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/cohort/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_cohort\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_cohort 26 | * @author Chris Pratt 27 | * @copyright Chris Pratt 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/cohort/lang/en/factor_cohort.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_cohort 21 | * @author Chris Pratt 22 | * @copyright Chris Pratt 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['pluginname'] = 'Cohort Factor'; 27 | $string['privacy:metadata'] = 'The cohort membership factor plugin does not store any personal data'; 28 | $string['settings:cohort'] = 'Non-passing cohorts'; 29 | $string['settings:cohort_help'] = 'Select the cohorts that will not pass this factor. This allows you to force these cohorts to use other factors to authenticate.'; 30 | $string['summarycondition'] = 'does NOT have any of the following cohorts assigned in any context: {$a}'; 31 | -------------------------------------------------------------------------------- /factor/cohort/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_cohort 21 | * @author Chris Pratt 22 | * @copyright Chris Pratt 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | require_once(__DIR__ . '/../../../../../cohort/lib.php'); 28 | 29 | $enabled = new admin_setting_configcheckbox('factor_cohort/enabled', 30 | new lang_string('settings:enablefactor', 'tool_mfa'), 31 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 32 | $enabled->set_updatedcallback(function () { 33 | \tool_mfa\manager::do_factor_action('cohort', get_config('factor_cohort', 'enabled') ? 'enable' : 'disable'); 34 | }); 35 | $settings->add($enabled); 36 | 37 | $settings->add(new admin_setting_configtext('factor_cohort/weight', 38 | new lang_string('settings:weight', 'tool_mfa'), 39 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 40 | 41 | $cohorts = cohort_get_all_cohorts(); 42 | $choices = []; 43 | 44 | foreach ($cohorts['cohorts'] as $cohort) { 45 | $choices[$cohort->id] = $cohort->name; 46 | } 47 | 48 | if (!empty($choices)) { 49 | $settings->add(new admin_setting_configmultiselect('factor_cohort/cohorts', 50 | new lang_string('settings:cohort', 'factor_cohort'), 51 | new lang_string('settings:cohort_help', 'factor_cohort'), [], $choices)); 52 | } 53 | -------------------------------------------------------------------------------- /factor/cohort/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_cohort 21 | * @subpackage tool_mfa 22 | * @author Chris Pratt 23 | * @copyright Chris Pratt 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2022101100; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_cohort'; 32 | $plugin->release = 'v0.2'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2022090600]; 35 | -------------------------------------------------------------------------------- /factor/email/classes/event/unauth_email.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_email\event; 18 | 19 | /** 20 | * Event for when a user receives an unauthorised email from MFA. 21 | * 22 | * @property-read array $other { 23 | * Extra information about event. 24 | * } 25 | * 26 | * @package factor_email 27 | * @author Peter Burnett 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class unauth_email extends \core\event\base { 32 | 33 | /** 34 | * Create instance of event. 35 | * 36 | * @param stdClass $user the User object of the User who passed all MFA factor checks. 37 | * @param string $ip the ip address the unauthorised email came from. 38 | * @param string $useragent the browser fingerpring the unauthorised email came from. 39 | * 40 | * @return user_passed_mfa the user_passed_mfa event 41 | * 42 | * @throws \coding_exception 43 | */ 44 | public static function unauth_email_event($user, $ip, $useragent) { 45 | 46 | $data = [ 47 | 'relateduserid' => null, 48 | 'context' => \context_user::instance($user->id), 49 | 'other' => [ 50 | 'userid' => $user->id, 51 | 'ip' => $ip, 52 | 'useragent' => $useragent, 53 | ], 54 | ]; 55 | 56 | return self::create($data); 57 | } 58 | 59 | /** 60 | * Init method. 61 | * 62 | * @return void 63 | */ 64 | protected function init() { 65 | $this->data['crud'] = 'r'; 66 | $this->data['edulevel'] = self::LEVEL_OTHER; 67 | } 68 | 69 | /** 70 | * Returns description of what happened. 71 | * 72 | * @return string 73 | */ 74 | public function get_description() { 75 | return "The user with id '{$this->other['userid']}' made an unauthorised login attempt using email verification from 76 | IP '{$this->other['ip']}' with browser agent '{$this->other['useragent']}'."; 77 | } 78 | 79 | /** 80 | * Return localised event name. 81 | * 82 | * @return string 83 | * @throws \coding_exception 84 | */ 85 | public static function get_name() { 86 | return get_string('event:unauthemail', 'factor_email'); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /factor/email/classes/form/email.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_email\form; 18 | 19 | defined('MOODLE_INTERNAL') || die(); 20 | 21 | require_once($CFG->libdir . "/formslib.php"); 22 | 23 | /** 24 | * Revoke email form. 25 | * 26 | * @package factor_email 27 | * @author Peter Burnett 28 | * @copyright Catalyst IT 29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | class email extends \moodleform { 32 | 33 | /** 34 | * Form definition. 35 | */ 36 | public function definition() { 37 | $mform = $this->_form; 38 | $mform->addElement('html', get_string('email:accident', 'factor_email')); 39 | $this->add_action_buttons(true, get_string('continue')); 40 | } 41 | 42 | /** 43 | * Form validation. 44 | * 45 | * Server side rules do not work for uploaded files, implement serverside rules here if needed. 46 | * 47 | * @param array $data array of ("fieldname"=>value) of submitted data 48 | * @param array $files array of uploaded files "element_name"=>tmp_file_path 49 | * @return array of "element_name"=>"error_description" if there are errors, 50 | * or an empty array if everything is OK (true allowed for backwards compatibility too). 51 | */ 52 | public function validation($data, $files) { 53 | $errors = parent::validation($data, $files); 54 | return $errors; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /factor/email/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_email\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_email 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/email/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_email 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_email/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('email', get_config('factor_email', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_email/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | 40 | $settings->add(new admin_setting_configduration('factor_email/duration', 41 | get_string('settings:duration', 'factor_email'), 42 | get_string('settings:duration_help', 'factor_email'), 30 * MINSECS, MINSECS)); 43 | 44 | $settings->add(new admin_setting_configcheckbox('factor_email/suspend', 45 | get_string('settings:suspend', 'factor_email'), 46 | get_string('settings:suspend_help', 'factor_email'), 0)); 47 | -------------------------------------------------------------------------------- /factor/email/templates/email.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template factor_email/email 19 | 20 | Template for email body sent to users. 21 | 22 | Example context (json): 23 | { 24 | "title": "Your verification code", 25 | "message": "Your verification code is 123456.", 26 | "ipinformation": "IP Information", 27 | "ip": "The IP was 127.0.0.1", 28 | "geoinfo": "Request originated from Brisbane, Australia", 29 | "uadescription": "User agent", 30 | "ua": "The user agent is Firefox 70", 31 | "linkstring": "If this wasn't you, click here" 32 | } 33 | }} 34 |

{{title}}

35 |

{{{message}}}

36 |

{{ipinformation}}

37 |

{{ip}}

38 |

{{geoinfo}}

39 |

{{uadescription}}

40 |

{{ua}}

41 |

{{{linkstring}}}

42 | -------------------------------------------------------------------------------- /factor/email/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_email 21 | * @subpackage tool_mfa 22 | * @author Mikhail Golenkov 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2023082100; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_email'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/grace/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_grace\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_grace 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/grace/classes/task/revoke_expired_factors.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Scheduled task to revoke expired factors 19 | * 20 | * @package factor_grace 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace factor_grace\task; 27 | 28 | /** 29 | * Scheduled task to revoke expired gracemode factors 30 | */ 31 | class revoke_expired_factors extends \core\task\scheduled_task { 32 | 33 | /** 34 | * Return the task's name as shown in admin screens. 35 | * 36 | * @return string 37 | */ 38 | public function get_name() { 39 | return get_string('revokeexpiredfactors', 'factor_grace'); 40 | } 41 | 42 | /** 43 | * Execute the task. 44 | * 45 | * @return void 46 | */ 47 | public function execute() { 48 | mtrace('Starting to revoke expired Grace factors'); 49 | $this->revoke_factors(); 50 | } 51 | 52 | /** 53 | * Revokes all grace factors that have a valid timecreated and are outside the duration. 54 | * 55 | * @return void 56 | */ 57 | private function revoke_factors() { 58 | global $DB; 59 | 60 | // If config is not set, pull out. 61 | $duration = get_config('factor_grace', 'graceperiod'); 62 | if (!$duration) { 63 | mtrace('Gracemode duration is not set. Exiting...'); 64 | return; 65 | } 66 | $revoketime = time() - $duration; 67 | 68 | // Single query implementation. 69 | $sql = "UPDATE {tool_mfa} 70 | SET revoked = 1, 71 | timemodified = :timemodified 72 | WHERE timecreated < :revoketime 73 | AND factor = :factor"; 74 | $DB->execute($sql, ['timemodified' => time(), 'revoketime' => $revoketime, 'factor' => 'grace']); 75 | 76 | mtrace('Finished revoking expired Grace factors'); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /factor/grace/db/tasks.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Task scheduler 19 | * 20 | * @package factor_grace 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $tasks = [ 29 | [ 30 | 'classname' => 'factor_grace\task\revoke_expired_factors', 31 | 'blocking' => 0, 32 | 'minute' => 'R', 33 | 'hour' => '0', 34 | 'day' => '*', 35 | 'month' => '*', 36 | 'dayofweek' => '*', 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /factor/grace/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_grace 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2022020401; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_grace'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/iprange/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_iprange; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * IP Range factor class. 23 | * 24 | * @package factor_iprange 25 | * @author Peter Burnett 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * IP Range Factor implementation. 33 | * This factor is a singleton, return single instance. 34 | * 35 | * @param stdClass $user the user to check against. 36 | * @return array 37 | */ 38 | public function get_all_user_factors($user) { 39 | return $this->get_singleton_user_factor($user); 40 | } 41 | 42 | /** 43 | * IP Range Factor implementation. 44 | * Factor has no input 45 | * 46 | * {@inheritDoc} 47 | */ 48 | public function has_input() { 49 | return false; 50 | } 51 | 52 | /** 53 | * IP Range Factor implementation. 54 | * Checks a users current IP against allowed and disallowed ranges. 55 | * 56 | * {@inheritDoc} 57 | */ 58 | public function get_state() { 59 | $safeips = get_config('factor_iprange', 'safeips'); 60 | 61 | // TODO: Check for failures here. 62 | 63 | if (!empty($safeips)) { 64 | if (remoteip_in_list($safeips)) { 65 | return \tool_mfa\plugininfo\factor::STATE_PASS; 66 | } 67 | } 68 | 69 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 70 | } 71 | 72 | /** 73 | * IP Range Factor implementation. 74 | * Cannot set state, return true. 75 | * 76 | * @param mixed $state the state constant to set 77 | * @return bool 78 | */ 79 | public function set_state($state) { 80 | return true; 81 | } 82 | 83 | /** 84 | * IP Range Factor implementation. 85 | * User can influence state prior to login. 86 | * Possible states are either neutral or pass. 87 | * 88 | * @param \stdClass $user 89 | */ 90 | public function possible_states($user) { 91 | return [ 92 | \tool_mfa\plugininfo\factor::STATE_PASS, 93 | \tool_mfa\plugininfo\factor::STATE_NEUTRAL, 94 | ]; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /factor/iprange/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_iprange\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_iprange 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/iprange/lang/en/factor_iprange.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_iprange 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['allowedipsempty'] = 'Nobody will pass this factor! You can add your own IP address ({$a->ip})'; 27 | $string['allowedipshasmyip'] = 'Your IP ({$a->ip}) is in the list and you will pass this factor.'; 28 | $string['allowedipshasntmyip'] = 'Your IP ({$a->ip}) is not in the list and you will not pass this factor.'; 29 | $string['pluginname'] = 'IP Range Factor'; 30 | $string['privacy:metadata'] = 'The IP Range Factor plugin does not store any personal data'; 31 | $string['settings:safeips'] = 'Safe IP ranges'; 32 | $string['settings:safeips_help'] = 'Enter a list of IP addresses or subnets to be counted as a pass in factor. If empty noone will pass this factor. {$a->info} {$a->syntax}'; 33 | $string['summarycondition'] = 'is on a secured network'; 34 | -------------------------------------------------------------------------------- /factor/iprange/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_iprange 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $OUTPUT; 29 | 30 | $enabled = new admin_setting_configcheckbox('factor_iprange/enabled', 31 | new lang_string('settings:enablefactor', 'tool_mfa'), 32 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 33 | $enabled->set_updatedcallback(function () { 34 | \tool_mfa\manager::do_factor_action('iprange', get_config('factor_iprange', 'enabled') ? 'enable' : 'disable'); 35 | }); 36 | $settings->add($enabled); 37 | 38 | $settings->add(new admin_setting_configtext('factor_iprange/weight', 39 | new lang_string('settings:weight', 'tool_mfa'), 40 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 41 | 42 | 43 | // Current IP validation against list for description. 44 | $allowedips = get_config('factor_iprange', 'safeips'); 45 | if (trim($allowedips) == '') { 46 | $message = 'allowedipsempty'; 47 | $type = 'notifyerror'; 48 | } else if (remoteip_in_list($allowedips)) { 49 | $message = 'allowedipshasmyip'; 50 | $type = 'notifysuccess'; 51 | } else { 52 | $message = 'allowedipshasntmyip'; 53 | $type = 'notifyerror'; 54 | }; 55 | $info = $OUTPUT->notification(get_string($message, 'factor_iprange', ['ip' => getremoteaddr()]), $type); 56 | 57 | $settings->add(new admin_setting_configiplist('factor_iprange/safeips', 58 | new lang_string('settings:safeips', 'factor_iprange'), 59 | new lang_string('settings:safeips_help', 'factor_iprange', 60 | ['info' => $info, 'syntax' => get_string('ipblockersyntax', 'admin')]), '', PARAM_TEXT)); 61 | 62 | -------------------------------------------------------------------------------- /factor/iprange/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_iprange 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX). 29 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 30 | $plugin->component = 'factor_iprange'; 31 | $plugin->release = 'v0.1'; 32 | $plugin->maturity = MATURITY_STABLE; 33 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 34 | -------------------------------------------------------------------------------- /factor/loginbanner/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_loginbanner; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * Policy factor class. 23 | * 24 | * @package factor_loginbanner 25 | * @author Peter Burnett 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * Login banner Factor implementation. 33 | * Factor is a singleton, can only be one instance. 34 | * 35 | * @param stdClass $user the user to check against. 36 | * @return array 37 | */ 38 | public function get_all_user_factors($user) { 39 | return $this->get_singleton_user_factor($user); 40 | } 41 | 42 | /** 43 | * Login banner factor implementation. 44 | * 45 | * {@inheritDoc} 46 | */ 47 | public function has_input() { 48 | return true; 49 | } 50 | 51 | /** 52 | * Login banner factor implementation. 53 | * 54 | * @param \MoodleQuickForm $mform 55 | * @return object $mform 56 | */ 57 | public function login_form_definition($mform) { 58 | $mform->addElement('html', get_string('policytext', 'factor_loginbanner')); 59 | return $mform; 60 | } 61 | 62 | /** 63 | * Login banner Factor implementation. 64 | * A cancel action is a decline for this factor. 65 | * This should result in an instant fail and unable to auth. 66 | * 67 | * {@inheritDoc} 68 | */ 69 | public function process_cancel_action() { 70 | global $CFG; 71 | 72 | $this->set_state(\tool_mfa\plugininfo\factor::STATE_FAIL); 73 | \tool_mfa\manager::mfa_logout(); 74 | redirect($CFG->wwwroot); 75 | } 76 | 77 | /** 78 | * Login banner factor implementation. 79 | * 80 | * @param \stdClass $user 81 | */ 82 | public function possible_states($user) { 83 | // Policy can only return a pass or fail when known. 84 | return [ 85 | \tool_mfa\plugininfo\factor::STATE_FAIL, 86 | \tool_mfa\plugininfo\factor::STATE_PASS, 87 | \tool_mfa\plugininfo\factor::STATE_UNKNOWN, 88 | ]; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /factor/loginbanner/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_loginbanner\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_loginbanner 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/loginbanner/lang/en/factor_loginbanner.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_loginbanner 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['info'] = 'This factor forces users to read and accept a policy, otherwise they will be unable to authenticate to the system'; 27 | $string['loginskip'] = 'Decline and log out'; 28 | $string['loginsubmit'] = 'Accept'; 29 | $string['pluginname'] = 'Login banner'; 30 | $string['policytext'] = '

This is a placeholder policy text. This can be overridden in the sites language pack configuration menu.

'; 31 | $string['privacy:metadata'] = 'The Policy factor plugin does not store any personal data'; 32 | $string['summarycondition'] = 'accepts the user policy'; 33 | -------------------------------------------------------------------------------- /factor/loginbanner/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Loginbanner factor Settings. 19 | * 20 | * @package factor_loginbanner 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_loginbanner/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('loginbanner', get_config('factor_loginbanner', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_loginbanner/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | -------------------------------------------------------------------------------- /factor/loginbanner/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_loginbanner 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2021030800; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->release = 2021030800; // Keep in lockstep with version. 31 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 32 | $plugin->component = 'factor_loginbanner'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/nosetup/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_nosetup\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_nosetup 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/nosetup/db/tasks.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Task scheduler 19 | * 20 | * @package factor_nosetup 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $tasks = [ 29 | [ 30 | 'classname' => 'factor_nosetup\task\delete_unusable_factors', 31 | 'blocking' => 0, 32 | 'minute' => 'R', 33 | 'hour' => '0', 34 | 'day' => '*', 35 | 'month' => '*', 36 | 'dayofweek' => '*', 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /factor/nosetup/lang/en/factor_nosetup.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_nosetup 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['deleteunusablefactors'] = 'Delete unusable Nosetup factors'; 27 | $string['info'] = 'This factor passes if the user has no other factors setup.'; 28 | $string['pluginname'] = 'No other factors'; 29 | $string['privacy:metadata'] = 'The No other factors plugin does not store any personal data'; 30 | $string['summarycondition'] = 'has no other factors setup'; 31 | -------------------------------------------------------------------------------- /factor/nosetup/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_nosetup 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_nosetup/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('nosetup', get_config('factor_nosetup', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_nosetup/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | -------------------------------------------------------------------------------- /factor/nosetup/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_nosetup 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2020042302; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_nosetup'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/role/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_role\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_role 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/role/lang/en/factor_role.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_role 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['pluginname'] = 'Role Factor'; 27 | $string['privacy:metadata'] = 'The user capability factor plugin does not store any personal data'; 28 | $string['settings:roles'] = 'Non-passing roles'; 29 | $string['settings:roles_help'] = 'Select the roles that will not pass this factor. This allows you to force these roles to use other factors to authenticate.'; 30 | $string['summarycondition'] = 'does NOT have any of the following roles assigned in any context: {$a}'; 31 | -------------------------------------------------------------------------------- /factor/role/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_role 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_role/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('role', get_config('factor_role', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_role/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | 40 | $choices = ['admin' => get_string('administrator')]; 41 | $roles = get_all_roles(); 42 | foreach ($roles as $role) { 43 | $choices[$role->id] = role_get_name($role); 44 | } 45 | 46 | $settings->add(new admin_setting_configmultiselect('factor_role/roles', 47 | new lang_string('settings:roles', 'factor_role'), 48 | new lang_string('settings:roles_help', 'factor_role'), ['admin'], $choices)); 49 | -------------------------------------------------------------------------------- /factor/role/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_role 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2020072100; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_role'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/secq/classes/factor.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_secq; 18 | 19 | use tool_mfa\local\factor\object_factor_base; 20 | 21 | /** 22 | * Security Question factor class. 23 | * 24 | * @package factor_secq 25 | * @author Peter Burnett 26 | * @copyright Catalyst IT 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class factor extends object_factor_base { 30 | 31 | /** 32 | * Security Questions Factor implementation. 33 | * 34 | * @param array $data 35 | * @return stdClass the factor record, or null. 36 | */ 37 | public function setup_user_factor($data) { 38 | // SETUP FUNCTIONALITY HERE. 39 | return true; 40 | } 41 | 42 | /** 43 | * Security Questions Factor implementation. 44 | * 45 | * @param stdClass $user the user to check against. 46 | * @return array 47 | */ 48 | public function get_all_user_factors($user) { 49 | // FACTOR FUNCTIONALITY HERE. 50 | return []; 51 | } 52 | 53 | /** 54 | * Security Questions Factor implementation. 55 | * 56 | * {@inheritDoc} 57 | */ 58 | public function has_input() { 59 | // CHANGE TO TRUE WHEN IMPLEMENTED. 60 | return false; 61 | } 62 | 63 | /** 64 | * Security Questions Factor implementation. 65 | * RETURN CORRECT STATE WHEN IMPLEMENTED. 66 | * 67 | * {@inheritDoc} 68 | */ 69 | public function get_state() { 70 | return \tool_mfa\plugininfo\factor::STATE_NEUTRAL; 71 | } 72 | 73 | /** 74 | * Security Questions Factor implementation. 75 | * 76 | * @param mixed $state the state constant to set 77 | * @return bool 78 | */ 79 | public function set_state($state) { 80 | // SET STATE CORRECTLY HERE. 81 | return true; 82 | } 83 | 84 | /** 85 | * Security Questions Factor Implementation. 86 | */ 87 | public function get_no_redirect_urls() { 88 | return [new \moodle_url('/admin/tool/securityquestions/set_responses.php')]; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /factor/secq/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_secq\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_secq 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/secq/lang/en/factor_secq.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_secq 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | $string['info'] = 'Answer security questions as a factor.'; 26 | $string['pluginname'] = 'Security questions'; 27 | $string['privacy:metadata'] = 'The security questions factor plugin does not store any personal data'; 28 | $string['summarycondition'] = 'answers security questions'; 29 | -------------------------------------------------------------------------------- /factor/secq/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_secq 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $enabled = new admin_setting_configcheckbox('factor_secq/enabled', 29 | new lang_string('settings:enablefactor', 'tool_mfa'), 30 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 31 | $enabled->set_updatedcallback(function () { 32 | \tool_mfa\manager::do_factor_action('secq', get_config('factor_secq', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_secq/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | -------------------------------------------------------------------------------- /factor/secq/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_secq 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2019102400; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_secq'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/sms/classes/event/sms_sent.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_sms\event; 18 | 19 | /** 20 | * Event for a sent SMS 21 | * 22 | * @package factor_sms 23 | * @author Alex Morris 24 | * @copyright Catalyst IT 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class sms_sent extends \core\event\base { 28 | 29 | /** 30 | * Init sms sent event 31 | */ 32 | protected function init() { 33 | $this->data['crud'] = 'r'; 34 | $this->data['edulevel'] = self::LEVEL_OTHER; 35 | } 36 | 37 | /** 38 | * Returns non-localised event description with id's for admin use only. 39 | * 40 | * @return string 41 | */ 42 | public function get_description() { 43 | return "The user with id '{$this->other['userid']}'" . 44 | " had a verification code sent to them via SMS
Information: {$this->other['debug']}"; 45 | } 46 | 47 | /** 48 | * Returns localised general event name. 49 | * 50 | * Override in subclass, we can not make it static and abstract at the same time. 51 | * 52 | * @return string 53 | */ 54 | public static function get_name() { 55 | return get_string('event:smssent', 'factor_sms'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /factor/sms/classes/helper.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_sms; 18 | 19 | /** 20 | * Helper class for shared sms gateway functions 21 | * 22 | * @package factor_sms 23 | * @author Alex Morris 24 | * @copyright Catalyst IT 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class helper { 28 | 29 | /** 30 | * This function internationalises a number to E.164 standard. 31 | * https://46elks.com/kb/e164 32 | * 33 | * @param string $phonenumber the phone number to format. 34 | * @return string the formatted phone number. 35 | */ 36 | public static function format_number(string $phonenumber): string { 37 | // Remove all whitespace, dashes and brackets. 38 | $phonenumber = preg_replace('/[ \(\)-]/', '', $phonenumber); 39 | 40 | // Number is already in international format. Do nothing. 41 | if (strpos($phonenumber, '+') === 0) { 42 | return $phonenumber; 43 | } 44 | 45 | // Strip leading 0 if found. 46 | if (strpos($phonenumber, '0') === 0) { 47 | $phonenumber = substr($phonenumber, 1); 48 | } 49 | 50 | // Prepend country code. 51 | $countrycode = get_config('factor_sms', 'countrycode'); 52 | $phonenumber = '+' . $countrycode . $phonenumber; 53 | 54 | return $phonenumber; 55 | } 56 | 57 | /** 58 | * Redact the phone number for displaying on screen. 59 | * 60 | * @param string $phonenumber the phone number 61 | * @return string the redacted phone number 62 | */ 63 | public static function redact_phonenumber(string $phonenumber): string { 64 | // Create partial num for display. 65 | $len = strlen($phonenumber); 66 | // Keep last 3 characters. 67 | $redacted = str_repeat('x', $len - 3); 68 | $redacted .= substr($phonenumber, -3); 69 | return $redacted; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /factor/sms/classes/local/smsgateway/gateway_interface.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * SMS Gateway interface 19 | * 20 | * @package factor_sms 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace factor_sms\local\smsgateway; 27 | 28 | interface gateway_interface { 29 | 30 | /** 31 | * Sends an SMS message 32 | * 33 | * @param string $messagecontent the content to send in the SMS message. 34 | * @param string $phonenumber the destination for the message. 35 | * @return bool true on message send success 36 | */ 37 | public function send_sms_message(string $messagecontent, string $phonenumber): bool; 38 | 39 | /** 40 | * Add gateway specific settings to the SMS factor settings page. 41 | * 42 | * @param \admin_settingpage $settings 43 | * @return void 44 | */ 45 | public static function add_settings($settings); 46 | 47 | /** 48 | * Returns whether or not the gateway is enabled 49 | * 50 | * @return bool 51 | */ 52 | public static function is_gateway_enabled(): bool; 53 | } 54 | -------------------------------------------------------------------------------- /factor/sms/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_sms\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_sms 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/sms/db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * factor_sms upgrade. 19 | * 20 | * @package factor_sms 21 | * @copyright Alex Morris 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * Factor sms upgrade helper function 27 | * 28 | * Sets the config of the gateway to aws_sns for factor sms 29 | * 30 | * @param int $oldversion 31 | */ 32 | function xmldb_factor_sms_upgrade($oldversion) { 33 | if ($oldversion < 2021081300) { 34 | set_config('gateway', 'aws_sns', 'factor_sms'); 35 | upgrade_plugin_savepoint(true, 2021081300, 'factor', 'sms'); 36 | } 37 | 38 | return true; 39 | } 40 | -------------------------------------------------------------------------------- /factor/sms/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_sms 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | global $CFG, $OUTPUT; 28 | 29 | $enabled = new admin_setting_configcheckbox('factor_sms/enabled', 30 | new lang_string('settings:enablefactor', 'tool_mfa'), 31 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 32 | $enabled->set_updatedcallback(function () { 33 | \tool_mfa\manager::do_factor_action('sms', get_config('factor_sms', 'enabled') ? 'enable' : 'disable'); 34 | }); 35 | $settings->add($enabled); 36 | 37 | $settings->add(new admin_setting_configtext('factor_sms/weight', 38 | new lang_string('settings:weight', 'tool_mfa'), 39 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 40 | 41 | $settings->add(new admin_setting_configduration('factor_sms/duration', 42 | get_string('settings:duration', 'tool_mfa'), 43 | get_string('settings:duration_help', 'tool_mfa'), 30 * MINSECS, MINSECS)); 44 | 45 | $codeslink = 'https://en.wikipedia.org/wiki/List_of_country_calling_codes'; 46 | $link = \html_writer::link($codeslink, $codeslink); 47 | 48 | $settings->add(new admin_setting_configtext('factor_sms/countrycode', 49 | get_string('settings:countrycode', 'factor_sms'), 50 | get_string('settings:countrycode_help', 'factor_sms', $link), '', PARAM_INT)); 51 | 52 | $gateways = [ 53 | 'aws_sns' => get_string('settings:aws', 'factor_sms'), 54 | 'modica' => get_string('settings:modica', 'factor_sms'), 55 | ]; 56 | 57 | $settings->add(new admin_setting_configselect('factor_sms/gateway', 58 | get_string('settings:gateway', 'factor_sms'), 59 | get_string('settings:gateway_help', 'factor_sms'), 60 | 'aws_sns', $gateways)); 61 | 62 | if (empty(get_config('factor_sms', 'gateway'))) { 63 | return; 64 | } 65 | 66 | $class = '\factor_sms\local\smsgateway\\' . get_config('factor_sms', 'gateway'); 67 | call_user_func($class . '::add_settings', $settings); 68 | -------------------------------------------------------------------------------- /factor/sms/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_sms 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2021081300; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_sms'; 32 | $plugin->release = 'v0.1'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/token/classes/event/token_created.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_token\event; 18 | /** 19 | * Event for a token being created for a user. 20 | * 21 | * @package factor_token 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | class token_created extends \core\event\base { 27 | 28 | /** 29 | * Create instance of event. 30 | * 31 | * @param stdClass $user the User object of the User who had the token creeated. 32 | * @param array $state an array of the state of the token. 33 | * 34 | * @return token_created the token_created_event event 35 | * 36 | * @throws \coding_exception 37 | */ 38 | public static function token_created_event($user, $state) { 39 | $data = [ 40 | 'relateduserid' => $user->id, 41 | 'context' => \context_user::instance($user->id), 42 | 'other' => [ 43 | 'userid' => $user->id, 44 | 'state' => json_encode($state), 45 | ], 46 | ]; 47 | 48 | return self::create($data); 49 | } 50 | 51 | /** 52 | * Init method. 53 | * 54 | * @return void 55 | */ 56 | protected function init() { 57 | $this->data['crud'] = 'c'; 58 | $this->data['edulevel'] = self::LEVEL_OTHER; 59 | } 60 | 61 | /** 62 | * Returns description of what happened. 63 | * 64 | * @return string 65 | */ 66 | public function get_description() { 67 | $info = json_decode($this->other['state']); 68 | $string = '
'; 69 | foreach ($info as $name => $value) { 70 | if ($name === 'expiry') { 71 | $value = userdate($value); 72 | } 73 | 74 | $string .= ucwords($name) . ': ' . $value . '
'; 75 | } 76 | 77 | return "The user with id '{$this->other['userid']}' had an MFA token stored on their device.
Information:" . $string; 78 | } 79 | 80 | /** 81 | * Return localised event name. 82 | * 83 | * @return string 84 | * @throws \coding_exception 85 | */ 86 | public static function get_name() { 87 | return get_string('event:token_created', 'factor_token'); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /factor/token/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_token\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_token 26 | * @author Peter Burnett 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/token/lang/en/factor_token.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_token 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['event:token_created'] = 'MFA token created.'; 27 | $string['form:trust'] = 'Trust this device for {$a}.'; 28 | $string['pluginname'] = 'Trust this device'; 29 | $string['privacy:metadata'] = 'The token factor plugin does not store any personal data.'; 30 | $string['settings:expireovernight'] = 'Expire trust overnight'; 31 | $string['settings:expireovernight_help'] = 'This forces tokens to expire overnight, preventing mid-day interruptions for users. Instead they will be asked to MFA authenticate at the start of a day after expiry.'; 32 | $string['settings:expiry'] = 'Trust duration'; 33 | $string['settings:expiry_help'] = 'The duration a device is trusted before requiring a new MFA authentication.'; 34 | $string['settings:adminexpiry'] = 'Administrator trust duration'; 35 | $string['settings:adminexpiry_help'] = 'The duration an administrator device is trusted before requiring a new MFA authentication. This allows tighter restrictions on site administrators, or no trust.'; 36 | $string['summarycondition'] = 'the user has previously trusted this device'; 37 | -------------------------------------------------------------------------------- /factor/token/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_token 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $enabled = new admin_setting_configcheckbox('factor_token/enabled', 30 | new lang_string('settings:enablefactor', 'tool_mfa'), 31 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 32 | $enabled->set_updatedcallback(function () { 33 | \tool_mfa\manager::do_factor_action('token', get_config('factor_token', 'enabled') ? 'enable' : 'disable'); 34 | }); 35 | $settings->add($enabled); 36 | 37 | $settings->add(new admin_setting_configtext('factor_token/weight', 38 | new lang_string('settings:weight', 'tool_mfa'), 39 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 40 | 41 | $settings->add(new admin_setting_configduration('factor_token/expiry', 42 | new lang_string('settings:expiry', 'factor_token'), 43 | new lang_string('settings:expiry_help', 'factor_token'), DAYSECS)); 44 | 45 | $settings->add(new admin_setting_configduration('factor_token/adminexpiry', 46 | new lang_string('settings:adminexpiry', 'factor_token'), 47 | new lang_string('settings:adminexpiry_help', 'factor_token'), DAYSECS)); 48 | 49 | $settings->add(new admin_setting_configcheckbox('factor_token/expireovernight', 50 | new lang_string('settings:expireovernight', 'factor_token'), 51 | new lang_string('settings:expireovernight_help', 'factor_token'), 1)); 52 | -------------------------------------------------------------------------------- /factor/token/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_token 21 | * @subpackage tool_mfa 22 | * @author Peter Burnett 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2023080100; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_token'; 32 | $plugin->release = 2022011700; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | 36 | // Fix version numbers that are higher than 4.3 core. 37 | if (!during_initial_install() && (int) get_config('factor_token', 'version') >= 2023100900) { 38 | set_config('version', '2023080100', 'factor_token'); 39 | } 40 | -------------------------------------------------------------------------------- /factor/totp/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_totp\privacy; 18 | 19 | use core_privacy\local\metadata\null_provider; 20 | use core_privacy\local\legacy_polyfill; 21 | 22 | /** 23 | * Privacy provider. 24 | * 25 | * @package factor_totp 26 | * @author Mikhail Golenkov 27 | * @copyright Catalyst IT 28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 | */ 30 | class provider implements null_provider { 31 | use legacy_polyfill; 32 | 33 | /** 34 | * Get the language string identifier with the component's language 35 | * file to explain why this plugin stores no data. 36 | * 37 | * @return string 38 | */ 39 | public static function _get_reason() { 40 | return 'privacy:metadata'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /factor/totp/extlib/Assert/AssertionFailedException.php: -------------------------------------------------------------------------------- 1 | propertyPath = $propertyPath; 39 | $this->value = $value; 40 | $this->constraints = $constraints; 41 | } 42 | 43 | /** 44 | * User controlled way to define a sub-property causing 45 | * the failure of a currently asserted objects. 46 | * 47 | * Useful to transport information about the nature of the error 48 | * back to higher layers. 49 | * 50 | * @return string|null 51 | */ 52 | public function getPropertyPath() 53 | { 54 | return $this->propertyPath; 55 | } 56 | 57 | /** 58 | * Get the value that caused the assertion to fail. 59 | * 60 | * @return mixed 61 | */ 62 | public function getValue() 63 | { 64 | return $this->value; 65 | } 66 | 67 | /** 68 | * Get the constraints that applied to the failed assertion. 69 | * 70 | * @return array 71 | */ 72 | public function getConstraints(): array 73 | { 74 | return $this->constraints; 75 | } 76 | } -------------------------------------------------------------------------------- /factor/totp/extlib/OTPHP/TOTPInterface.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_totp 21 | * @subpackage tool_mfa 22 | * @author Mikhail Golenkov 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $enabled = new admin_setting_configcheckbox('factor_totp/enabled', 30 | new lang_string('settings:enablefactor', 'tool_mfa'), 31 | new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); 32 | $enabled->set_updatedcallback(function () { 33 | \tool_mfa\manager::do_factor_action('totp', get_config('factor_totp', 'enabled') ? 'enable' : 'disable'); 34 | }); 35 | $settings->add($enabled); 36 | 37 | $settings->add(new admin_setting_configtext('factor_totp/weight', 38 | new lang_string('settings:weight', 'tool_mfa'), 39 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 40 | 41 | $settings->add(new admin_setting_configduration('factor_totp/window', 42 | new lang_string('settings:window', 'factor_totp'), 43 | new lang_string('settings:window_help', 'factor_totp'), 30)); 44 | 45 | $settings->add(new admin_setting_configcheckbox('factor_totp/totplink', 46 | new lang_string('settings:totplink', 'factor_totp'), 47 | new lang_string('settings:totplink_help', 'factor_totp'), 1)); 48 | -------------------------------------------------------------------------------- /factor/totp/thirdpartylibs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | extlib/Assert 5 | Assert 6 | 7 | MIT 8 | 2.1+ 9 | 10 | 11 | extlib/OTPHP 12 | OTPHP 13 | 14 | MIT 15 | 2.1+ 16 | 17 | 18 | extlib/ParagonIE 19 | ParagonIE 20 | 21 | Custom 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /factor/totp/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_totp 21 | * @subpackage tool_mfa 22 | * @author Mikhail Golenkov 23 | * @copyright Catalyst IT 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $plugin->version = 2021021700; // The current plugin version (Date: YYYYMMDDXX). 30 | $plugin->release = 2021021700; 31 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 32 | $plugin->component = 'factor_totp'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | $plugin->dependencies = ['tool_mfa' => 2019102400]; 35 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/.gitignore: -------------------------------------------------------------------------------- 1 | # Netbeans project 2 | nbproject/ 3 | /index.php 4 | 5 | 6 | # .pem files from FIDO Alliance Metadata Service (MDS) 7 | _test/rootCertificates/mds/*.pem 8 | _test/rootCertificates/mds/lastMdsFetch.txt 9 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2022 Lukas Buchs 4 | Copyright © 2018 Thomas Bleeker (CBOR & ByteBuffer part) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/apple.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 | 68:1d:01:6c:7a:3c:e3:02:25:a5:01:94:28:47:57:71 6 | 7 | Signature Algorithm: ecdsa-with-SHA384 8 | 9 | Issuer: 10 | stateOrProvinceName = California 11 | organizationName = Apple Inc. 12 | commonName = Apple WebAuthn Root CA 13 | 14 | Validity 15 | Not Before: Mar 18 18:21:32 2020 GMT 16 | Not After : Mar 15 00:00:00 2045 GMT 17 | 18 | Subject: 19 | stateOrProvinceName = California 20 | organizationName = Apple Inc. 21 | commonName = Apple WebAuthn Root CA 22 | 23 | Subject Public Key Info: 24 | Public Key Algorithm: id-ecPublicKey 25 | ASN1 OID: secp384r1 26 | 27 | X509v3 extensions: 28 | X509v3 Basic Constraints: critical 29 | CA:TRUE 30 | X509v3 Subject Key Identifier: 31 | 26:D7:64:D9:C5:78:C2:5A:67:D1:A7:DE:6B:12:D0:1B:63:F1:C6:D7 32 | X509v3 Key Usage: critical 33 | Certificate Sign, CRL Sign 34 | 35 | -----BEGIN CERTIFICATE----- 36 | MIICEjCCAZmgAwIBAgIQaB0BbHo84wIlpQGUKEdXcTAKBggqhkjOPQQDAzBLMR8w 37 | HQYDVQQDDBZBcHBsZSBXZWJBdXRobiBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ 38 | bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTIwMDMxODE4MjEzMloXDTQ1MDMx 39 | NTAwMDAwMFowSzEfMB0GA1UEAwwWQXBwbGUgV2ViQXV0aG4gUm9vdCBDQTETMBEG 40 | A1UECgwKQXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTB2MBAGByqGSM49 41 | AgEGBSuBBAAiA2IABCJCQ2pTVhzjl4Wo6IhHtMSAzO2cv+H9DQKev3//fG59G11k 42 | xu9eI0/7o6V5uShBpe1u6l6mS19S1FEh6yGljnZAJ+2GNP1mi/YK2kSXIuTHjxA/ 43 | pcoRf7XkOtO4o1qlcaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJtdk 44 | 2cV4wlpn0afeaxLQG2PxxtcwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA 45 | MGQCMFrZ+9DsJ1PW9hfNdBywZDsWDbWFp28it1d/5w2RPkRX3Bbn/UbDTNLx7Jr3 46 | jAGGiQIwHFj+dJZYUJR786osByBelJYsVZd2GbHQu209b5RCmGQ21gpSAk9QZW4B 47 | 1bWeT0vT 48 | -----END CERTIFICATE----- 49 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/globalSign.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 | 04:00:00:00:00:01:0f:86:26:e6:0d 6 | Signature Algorithm: sha1WithRSAEncryption 7 | Issuer: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign 8 | Validity 9 | Not Before: Dec 15 08:00:00 2006 GMT 10 | Not After : Dec 15 08:00:00 2021 GMT 11 | Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign 12 | Subject Public Key Info: 13 | Public Key Algorithm: rsaEncryption 14 | Public-Key: (2048 bit) 15 | 16 | -----BEGIN CERTIFICATE----- 17 | MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G 18 | A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp 19 | Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 20 | MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG 21 | A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI 22 | hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL 23 | v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 24 | eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq 25 | tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd 26 | C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa 27 | zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB 28 | mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH 29 | V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n 30 | bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG 31 | 3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs 32 | J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO 33 | 291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS 34 | ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd 35 | AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 36 | TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== 37 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/hypersecu.pem: -------------------------------------------------------------------------------- 1 | HyperFIDO U2F Security Key Attestation CA 2 | https://hypersecu.com/support/downloads/attestation 3 | 4 | Last Update: 2017-01-01 5 | 6 | HyperFIDO U2F Security Key devices which contain attestation certificates signed by a set of CAs. 7 | This file contains the CA certificates that Relying Parties (RP) need to configure their software 8 | with to be able to verify U2F device certificates. 9 | 10 | The file will be updated as needed when we publish more CA certificates. 11 | 12 | Issuer: CN=FT FIDO 0100 13 | 14 | -----BEGIN CERTIFICATE----- 15 | MIIBjTCCATOgAwIBAgIBATAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxGVCBGSURP 16 | IDAxMDAwHhcNMTQwNzAxMTUzNjI2WhcNNDQwNzAzMTUzNjI2WjAXMRUwEwYDVQQD 17 | EwxGVCBGSURPIDAxMDAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASxdLxJx8ol 18 | S3DS5cIHzunPF0gg69d+o8ZVCMJtpRtlfBzGuVL4YhaXk2SC2gptPTgmpZCV2vbN 19 | fAPi5gOF0vbZo3AwbjAdBgNVHQ4EFgQUXt4jWlYDgwhaPU+EqLmeM9LoPRMwPwYD 20 | VR0jBDgwNoAUXt4jWlYDgwhaPU+EqLmeM9LoPROhG6QZMBcxFTATBgNVBAMTDEZU 21 | IEZJRE8gMDEwMIIBATAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQC2 22 | D9o9cconKTo8+4GZPyZBJ3amc8F0/kzyidX9dhrAIAIgM9ocs5BW/JfmshVP9Mb+ 23 | Joa/kgX4dWbZxrk0ioTfJZg= 24 | -----END CERTIFICATE----- 25 | 26 | 27 | Certificate: 28 | Data: 29 | Version: 3 (0x2) 30 | Serial Number: 4107 (0x100b) 31 | Signature Algorithm: ecdsa-with-SHA256 32 | Issuer: 33 | commonName = HYPERFIDO 0200 34 | organizationName = HYPERSECU 35 | countryName = CA 36 | Validity 37 | Not Before: Jan 1 00:00:00 2018 GMT 38 | Not After : Dec 31 23:59:59 2047 GMT 39 | Subject: 40 | commonName = HYPERFIDO 0200 41 | organizationName = HYPERSECU 42 | countryName = CA 43 | 44 | 45 | -----BEGIN CERTIFICATE----- 46 | MIIBxzCCAWygAwIBAgICEAswCgYIKoZIzj0EAwIwOjELMAkGA1UEBhMCQ0ExEjAQ 47 | BgNVBAoMCUhZUEVSU0VDVTEXMBUGA1UEAwwOSFlQRVJGSURPIDAyMDAwIBcNMTgw 48 | MTAxMDAwMDAwWhgPMjA0NzEyMzEyMzU5NTlaMDoxCzAJBgNVBAYTAkNBMRIwEAYD 49 | VQQKDAlIWVBFUlNFQ1UxFzAVBgNVBAMMDkhZUEVSRklETyAwMjAwMFkwEwYHKoZI 50 | zj0CAQYIKoZIzj0DAQcDQgAErKUI1G0S7a6IOLlmHipLlBuxTYjsEESQvzQh3dB7 51 | dvxxWWm7kWL91rq6S7ayZG0gZPR+zYqdFzwAYDcG4+aX66NgMF4wHQYDVR0OBBYE 52 | FLZYcfMMwkQAGbt3ryzZFPFypmsIMB8GA1UdIwQYMBaAFLZYcfMMwkQAGbt3ryzZ 53 | FPFypmsIMAwGA1UdEwQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMC 54 | A0kAMEYCIQCG2/ppMGt7pkcRie5YIohS3uDPIrmiRcTjqDclKVWg0gIhANcPNDZH 55 | E2/zZ+uB5ThG9OZus+xSb4knkrbAyXKX2zm/ 56 | -----END CERTIFICATE----- 57 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/mds/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/solo.pem: -------------------------------------------------------------------------------- 1 | Solokeys FIDO2/U2F Device Attestation CA 2 | ======================================== 3 | Data: 4 | Version: 1 (0x0) 5 | Serial Number: 14143382635911888524 (0xc44763928ff4be8c) 6 | Signature Algorithm: ecdsa-with-SHA256 7 | 8 | Issuer: 9 | emailAddress = hello@solokeys.com 10 | commonName = solokeys.com 11 | organizationalUnitName = Root CA 12 | organizationName = Solo Keys 13 | stateOrProvinceName = Maryland 14 | countryName = US 15 | 16 | Validity 17 | Not Before: Nov 11 12:51:42 2018 GMT 18 | Not After : Oct 29 12:51:42 2068 GMT 19 | 20 | Subject: 21 | emailAddress = hello@solokeys.com 22 | commonName = solokeys.com 23 | organizationalUnitName = Root CA 24 | organizationName = Solo Keys 25 | stateOrProvinceName = Maryland 26 | countryName = US 27 | 28 | 29 | -----BEGIN CERTIFICATE----- 30 | MIIB9DCCAZoCCQDER2OSj/S+jDAKBggqhkjOPQQDAjCBgDELMAkGA1UEBhMCVVMx 31 | ETAPBgNVBAgMCE1hcnlsYW5kMRIwEAYDVQQKDAlTb2xvIEtleXMxEDAOBgNVBAsM 32 | B1Jvb3QgQ0ExFTATBgNVBAMMDHNvbG9rZXlzLmNvbTEhMB8GCSqGSIb3DQEJARYS 33 | aGVsbG9Ac29sb2tleXMuY29tMCAXDTE4MTExMTEyNTE0MloYDzIwNjgxMDI5MTI1 34 | MTQyWjCBgDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1hcnlsYW5kMRIwEAYDVQQK 35 | DAlTb2xvIEtleXMxEDAOBgNVBAsMB1Jvb3QgQ0ExFTATBgNVBAMMDHNvbG9rZXlz 36 | LmNvbTEhMB8GCSqGSIb3DQEJARYSaGVsbG9Ac29sb2tleXMuY29tMFkwEwYHKoZI 37 | zj0CAQYIKoZIzj0DAQcDQgAEWHAN0CCJVZdMs0oktZ5m93uxmB1iyq8ELRLtqVFL 38 | SOiHQEab56qRTB/QzrpGAY++Y2mw+vRuQMNhBiU0KzwjBjAKBggqhkjOPQQDAgNI 39 | ADBFAiEAz9SlrAXIlEu87vra54rICPs+4b0qhp3PdzcTg7rvnP0CIGjxzlteQQx+ 40 | jQGd7rwSZuE5RWUPVygYhUstQO9zNUOs 41 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/_test/rootCertificates/yubico.pem: -------------------------------------------------------------------------------- 1 | Yubico U2F Device Attestation CA 2 | ================================ 3 | 4 | Last Update: 2014-09-01 5 | 6 | Yubico manufacturer U2F devices that contains device attestation 7 | certificates signed by a set of Yubico CAs. This file contains the CA 8 | certificates that Relying Parties (RP) need to configure their 9 | software with to be able to verify U2F device certificates. 10 | 11 | This file has been signed with OpenPGP and you should verify the 12 | signature and the authenticity of the public key before trusting the 13 | content. The signature is located next to the file: 14 | 15 | https://developers.yubico.com/u2f/yubico-u2f-ca-certs.txt 16 | https://developers.yubico.com/u2f/yubico-u2f-ca-certs.txt.sig 17 | 18 | We will update this file from time to time when we publish more CA 19 | certificates. 20 | 21 | Name: Yubico U2F Root CA Serial 457200631 22 | Issued: 2014-08-01 23 | 24 | -----BEGIN CERTIFICATE----- 25 | MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ 26 | dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw 27 | MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290 28 | IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 29 | AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk 30 | 5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep 31 | 8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw 32 | nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT 33 | 9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw 34 | LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ 35 | hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN 36 | BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4 37 | MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt 38 | hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k 39 | LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U 40 | sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc 41 | U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw== 42 | -----END CERTIFICATE----- 43 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lbuchs/webauthn", 3 | "description": "A simple PHP WebAuthn (FIDO2) server library", 4 | "keywords": [ 5 | "webauthn", "authentication" 6 | ], 7 | "homepage": "https://github.com/lbuchs/webauthn", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Lukas Buchs", 12 | "role": "Developer" 13 | } 14 | ], 15 | "require": { 16 | "php" : ">=8.0.0" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "lbuchs\\WebAuthn\\": "src" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /factor/webauthn/.extlib/WebAuthn/src/Attestation/Format/None.php: -------------------------------------------------------------------------------- 1 | 8 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9 | */(utils);_exports.init=initialArgs=>{document.addEventListener("click",(async e=>{if(!e.target.closest("#id_submitbutton"))return;if(!navigator.credentials||!navigator.credentials.create)throw new Error("This browser does not support webauthn.");const getArgs=JSON.parse(initialArgs);if(!1===getArgs.success)throw new Error(getArgs.msg||"unknown error occured");e.preventDefault(),utils.recursiveBase64StrToArrayBuffer(getArgs);const authenticatorAttestationResponse=(cred=>{var _cred$response,_cred$response2,_cred$response3,_cred$response4;const response={id:null==cred?void 0:cred.rawId,clientDataJSON:null===(_cred$response=cred.response)||void 0===_cred$response?void 0:_cred$response.clientDataJSON,authenticatorData:null===(_cred$response2=cred.response)||void 0===_cred$response2?void 0:_cred$response2.authenticatorData,signature:null===(_cred$response3=cred.response)||void 0===_cred$response3?void 0:_cred$response3.signature,userHandle:null===(_cred$response4=cred.response)||void 0===_cred$response4?void 0:_cred$response4.userHandle};return Object.entries(response).forEach((_ref=>{let[key,value]=_ref;value&&(response[key]=utils.arrayBufferToBase64(value))})),response})(await navigator.credentials.get(getArgs));document.getElementById("id_response_input").value=JSON.stringify(authenticatorAttestationResponse),document.getElementById("id_response_input").form.submit()}))}})); 10 | 11 | //# sourceMappingURL=login.min.js.map -------------------------------------------------------------------------------- /factor/webauthn/amd/build/register.min.js: -------------------------------------------------------------------------------- 1 | define("factor_webauthn/register",["exports","./utils"],(function(_exports,utils){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,utils=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj} 2 | /** 3 | * For collecting WebAuthn authenticator details on factor setup 4 | * 5 | * @module factor_webauthn/register 6 | * @copyright Catalyst IT 7 | * @author Alex Morris 8 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9 | */(utils);_exports.init=initialCreateArgs=>{document.getElementById("id_submitbutton").disabled=!0,document.addEventListener("click",(async e=>{if(!e.target.closest("#factor_webauthn-register"))return;if(!navigator.credentials||!navigator.credentials.create)throw new Error("Browser not supported.");const createArgs=JSON.parse(initialCreateArgs);if(!1===createArgs.success)throw new Error(createArgs.msg||"unknown error occured");e.preventDefault(),utils.recursiveBase64StrToArrayBuffer(createArgs);const cred=await navigator.credentials.create(createArgs),authenticatorResponse={transports:cred.response.getTransports?cred.response.getTransports():null,clientDataJSON:cred.response.clientDataJSON?utils.arrayBufferToBase64(cred.response.clientDataJSON):null,attestationObject:cred.response.attestationObject?utils.arrayBufferToBase64(cred.response.attestationObject):null},inputResponse=document.getElementById("id_response_input");inputResponse.value=JSON.stringify(authenticatorResponse),document.getElementById("id_submitbutton").disabled=!1,inputResponse.form.elements.submitbutton.click()}))}})); 10 | 11 | //# sourceMappingURL=register.min.js.map -------------------------------------------------------------------------------- /factor/webauthn/amd/build/utils.min.js: -------------------------------------------------------------------------------- 1 | define("factor_webauthn/utils",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.recursiveBase64StrToArrayBuffer=_exports.arrayBufferToBase64=void 0; 2 | /** 3 | * WebAuthn utility functions, for handling array buffers. 4 | * 5 | * @module factor_webauthn/utils 6 | * @copyright Catalyst IT 7 | * @author Alex Morris 8 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9 | */ 10 | const recursiveBase64StrToArrayBuffer=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);const binaryString=window.atob(str),len=binaryString.length,bytes=new Uint8Array(len);for(let i=0;i{let binary="";const bytes=new Uint8Array(buffer),len=bytes.byteLength;for(let i=0;i.\n\n/**\n * WebAuthn utility functions, for handling array buffers.\n *\n * @module factor_webauthn/utils\n * @copyright Catalyst IT\n * @author Alex Morris \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst prefix = '=?BINARY?B?';\nconst suffix = '?=';\n\nexport const recursiveBase64StrToArrayBuffer = (obj) => {\n if (typeof obj !== 'object') {\n return;\n }\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 const binaryString = window.atob(str);\n const len = binaryString.length;\n const 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 } else {\n recursiveBase64StrToArrayBuffer(obj[key]);\n }\n }\n};\n\nexport const arrayBufferToBase64 = (buffer) => {\n let binary = '';\n const bytes = new Uint8Array(buffer);\n const 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"],"names":["recursiveBase64StrToArrayBuffer","obj","key","str","substring","length","binaryString","window","atob","len","bytes","Uint8Array","i","charCodeAt","buffer","binary","byteLength","String","fromCharCode","btoa"],"mappings":";;;;;;;;;MA2BaA,gCAAmCC,SACzB,iBAARA,QAGN,IAAIC,OAAOD,OACY,iBAAbA,IAAIC,KAAmB,KAC1BC,IAAMF,IAAIC,QATX,gBAUCC,IAAIC,UAAU,EAVf,cAUyBC,SATzB,OAS+CF,IAAIC,UAAUD,IAAIE,OATjE,KASiFA,QAAoB,CACpGF,IAAMA,IAAIC,UAXX,cAW4BC,OAAQF,IAAIE,OAVxC,KAUwDA,cAEjDC,aAAeC,OAAOC,KAAKL,KAC3BM,IAAMH,aAAaD,OACnBK,MAAQ,IAAIC,WAAWF,SACxB,IAAIG,EAAI,EAAGA,EAAIH,IAAKG,IACrBF,MAAME,GAAKN,aAAaO,WAAWD,GAEvCX,IAAIC,KAAOQ,MAAMI,aAGrBd,gCAAgCC,IAAIC,6GAKZY,aAC5BC,OAAS,SACPL,MAAQ,IAAIC,WAAWG,QACvBL,IAAMC,MAAMM,eACb,IAAIJ,EAAI,EAAGA,EAAIH,IAAKG,IACrBG,QAAUE,OAAOC,aAAaR,MAAME,WAEjCL,OAAOY,KAAKJ"} -------------------------------------------------------------------------------- /factor/webauthn/amd/src/login.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * For collecting WebAuthn authenticator details on login 18 | * 19 | * @module factor_webauthn/login 20 | * @copyright Catalyst IT 21 | * @author Alex Morris 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | import * as utils from './utils'; 26 | 27 | const getAttestationResponse = (cred) => { 28 | const response = { 29 | id: cred?.rawId, 30 | clientDataJSON: cred.response?.clientDataJSON, 31 | authenticatorData: cred.response?.authenticatorData, 32 | signature: cred.response?.signature, 33 | userHandle: cred.response?.userHandle, 34 | }; 35 | 36 | Object.entries(response).forEach(([key, value]) => { 37 | if (value) { 38 | response[key] = utils.arrayBufferToBase64(value); 39 | } 40 | }); 41 | 42 | return response; 43 | }; 44 | 45 | export const init = (initialArgs) => { 46 | document.addEventListener('click', async(e) => { 47 | if (!e.target.closest('#id_submitbutton')) { 48 | return; 49 | } 50 | 51 | if (!navigator.credentials || !navigator.credentials.create) { 52 | throw new Error('This browser does not support webauthn.'); 53 | } 54 | 55 | const getArgs = JSON.parse(initialArgs); 56 | if (getArgs.success === false) { 57 | throw new Error(getArgs.msg || 'unknown error occured'); 58 | } 59 | 60 | e.preventDefault(); 61 | 62 | utils.recursiveBase64StrToArrayBuffer(getArgs); 63 | 64 | const cred = await navigator.credentials.get(getArgs); 65 | const authenticatorAttestationResponse = getAttestationResponse(cred); 66 | 67 | document.getElementById('id_response_input').value = JSON.stringify(authenticatorAttestationResponse); 68 | document.getElementById('id_response_input').form.submit(); 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /factor/webauthn/amd/src/register.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * For collecting WebAuthn authenticator details on factor setup 18 | * 19 | * @module factor_webauthn/register 20 | * @copyright Catalyst IT 21 | * @author Alex Morris 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | import * as utils from './utils'; 26 | 27 | export const init = (initialCreateArgs) => { 28 | document.getElementById('id_submitbutton').disabled = true; 29 | document.addEventListener('click', async(e) => { 30 | if (!e.target.closest('#factor_webauthn-register')) { 31 | return; 32 | } 33 | 34 | if (!navigator.credentials || !navigator.credentials.create) { 35 | throw new Error('Browser not supported.'); 36 | } 37 | 38 | const createArgs = JSON.parse(initialCreateArgs); 39 | if (createArgs.success === false) { 40 | throw new Error(createArgs.msg || 'unknown error occured'); 41 | } 42 | e.preventDefault(); 43 | 44 | utils.recursiveBase64StrToArrayBuffer(createArgs); 45 | 46 | const cred = await navigator.credentials.create(createArgs); 47 | 48 | const authenticatorResponse = { 49 | transports: cred.response.getTransports ? cred.response.getTransports() : null, 50 | clientDataJSON: cred.response.clientDataJSON ? utils.arrayBufferToBase64(cred.response.clientDataJSON) : null, 51 | attestationObject: cred.response.attestationObject ? utils.arrayBufferToBase64(cred.response.attestationObject) : null, 52 | }; 53 | 54 | const inputResponse = document.getElementById('id_response_input'); 55 | inputResponse.value = JSON.stringify(authenticatorResponse); 56 | document.getElementById('id_submitbutton').disabled = false; 57 | 58 | // Do not use form.submit as it bypasses the change checker submit listener. 59 | inputResponse.form.elements.submitbutton.click(); 60 | }); 61 | }; 62 | -------------------------------------------------------------------------------- /factor/webauthn/amd/src/utils.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * WebAuthn utility functions, for handling array buffers. 18 | * 19 | * @module factor_webauthn/utils 20 | * @copyright Catalyst IT 21 | * @author Alex Morris 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | const prefix = '=?BINARY?B?'; 26 | const suffix = '?='; 27 | 28 | export const recursiveBase64StrToArrayBuffer = (obj) => { 29 | if (typeof obj !== 'object') { 30 | return; 31 | } 32 | for (let key in obj) { 33 | if (typeof obj[key] === 'string') { 34 | let str = obj[key]; 35 | if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) { 36 | str = str.substring(prefix.length, str.length - suffix.length); 37 | 38 | const binaryString = window.atob(str); 39 | const len = binaryString.length; 40 | const bytes = new Uint8Array(len); 41 | for (let i = 0; i < len; i++) { 42 | bytes[i] = binaryString.charCodeAt(i); 43 | } 44 | obj[key] = bytes.buffer; 45 | } 46 | } else { 47 | recursiveBase64StrToArrayBuffer(obj[key]); 48 | } 49 | } 50 | }; 51 | 52 | export const arrayBufferToBase64 = (buffer) => { 53 | let binary = ''; 54 | const bytes = new Uint8Array(buffer); 55 | const len = bytes.byteLength; 56 | for (let i = 0; i < len; i++) { 57 | binary += String.fromCharCode(bytes[i]); 58 | } 59 | return window.btoa(binary); 60 | }; 61 | -------------------------------------------------------------------------------- /factor/webauthn/classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace factor_webauthn\privacy; 18 | 19 | /** 20 | * Privacy provider. 21 | * 22 | * @package factor_webauthn 23 | * @author Alex Morris 24 | * @copyright Catalyst IT 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class provider implements \core_privacy\local\metadata\null_provider { 28 | 29 | /** 30 | * Get the language string identifier with the component's language 31 | * file to explain why this plugin stores no data. 32 | * 33 | * @return string 34 | */ 35 | public static function get_reason() : string { 36 | return 'privacy:metadata'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /factor/webauthn/lang/en/factor_webauthn.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Language strings. 19 | * 20 | * @package factor_webauthn 21 | * @author Alex Morris 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['action:revoke'] = 'Revoke security key'; 27 | $string['authenticator:ble'] = 'BLE'; 28 | $string['authenticator:hybrid'] = 'Hybrid'; 29 | $string['authenticator:internal'] = 'Internal'; 30 | $string['authenticator:nfc'] = 'NFC'; 31 | $string['authenticator:usb'] = 'USB'; 32 | $string['authenticatorname'] = 'Security key name'; 33 | $string['authenticatortypelimitation'] = 'Please note that you can only use security keys of one of these types: {$a}.
Registering other security keys is possible, but you cannot use them during login.'; 34 | $string['error'] = 'Failed to authenticate'; 35 | $string['info'] = '

Use a security key

'; 36 | $string['loginexplanation'] = 'Your account settings require that you authenticate with your security key in addition to your password.'; 37 | $string['loginskip'] = 'I don\'t have my security key'; 38 | $string['loginsubmit'] = 'Verify security key'; 39 | $string['pluginname'] = 'Security Key'; 40 | $string['privacy:metadata'] = 'The WebAuthn factor plugin does not store any personal data'; 41 | $string['register'] = 'Register security key'; 42 | $string['settings:authenticatortypes'] = 'Types of security keys'; 43 | $string['settings:authenticatortypes_help'] = 'Toggle certain types of security keys'; 44 | $string['settings:userverification'] = 'User verification'; 45 | $string['settings:userverification_help'] = 'Serves to ensure the person authenticating is in fact who they say they are. User verification can take various forms, such as password, PIN, fingerprint, etc.'; 46 | $string['setupfactor'] = 'Setup security key'; 47 | $string['summarycondition'] = 'using a security key'; 48 | $string['userverification:discouraged'] = 'User verification should not be employed, for example to minimize user interaction'; 49 | $string['userverification:preferred'] = 'User verification is preferred, authentication will not fail if user verification is missing'; 50 | $string['userverification:required'] = 'User verification is required (e.g. by pin). Authentication fails if key does not have user verification'; 51 | $string['userverificationrequired'] = 'Your security key must support user verification (e.g. pin input) to be eligible for registration.'; 52 | -------------------------------------------------------------------------------- /factor/webauthn/settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Settings 19 | * 20 | * @package factor_webauthn 21 | * @author Alex Morris set_updatedcallback(function() { 32 | \tool_mfa\manager::do_factor_action('webauthn', get_config('factor_webauthn', 'enabled') ? 'enable' : 'disable'); 33 | }); 34 | $settings->add($enabled); 35 | 36 | $settings->add(new admin_setting_configtext('factor_webauthn/weight', 37 | new lang_string('settings:weight', 'tool_mfa'), 38 | new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); 39 | 40 | $authenticators = [ 41 | 'usb' => get_string('authenticator:usb', 'factor_webauthn'), 42 | 'nfc' => get_string('authenticator:nfc', 'factor_webauthn'), 43 | 'ble' => get_string('authenticator:ble', 'factor_webauthn'), 44 | 'hybrid' => get_string('authenticator:hybrid', 'factor_webauthn'), 45 | 'internal' => get_string('authenticator:internal', 'factor_webauthn'), 46 | ]; 47 | $settings->add(new admin_setting_configmultiselect('factor_webauthn/authenticatortypes', 48 | new lang_string('settings:authenticatortypes', 'factor_webauthn'), 49 | new lang_string('settings:authenticatortypes_help', 'factor_webauthn'), 50 | array_keys($authenticators), $authenticators)); 51 | 52 | $settings->add(new admin_setting_configselect('factor_webauthn/userverification', 53 | new lang_string('settings:userverification', 'factor_webauthn'), 54 | new lang_string('settings:userverification_help', 'factor_webauthn'), 55 | 'preferred', 56 | $userverification = [ 57 | 'required' => get_string('userverification:required', 'factor_webauthn'), 58 | 'preferred' => get_string('userverification:preferred', 'factor_webauthn'), 59 | 'discouraged' => get_string('userverification:discouraged', 'factor_webauthn'), 60 | ])); 61 | -------------------------------------------------------------------------------- /factor/webauthn/thirdpartylibs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .extlib/WebAuthn 5 | WebAuthn 6 | 2.0.1 7 | MIT 8 | https://github.com/lbuchs/WebAuthn 9 | 10 | 11 | -------------------------------------------------------------------------------- /factor/webauthn/version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package factor_webauthn 21 | * @author Alex Morris version = 2023052400; // The current plugin version (Date: YYYYMMDDXX). 29 | $plugin->release = 2023052400; 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'factor_webauthn'; 32 | $plugin->maturity = MATURITY_ALPHA; 33 | $plugin->dependencies = ['tool_mfa' => 2023031600]; 34 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | tests 21 | 22 | 23 | ../../../privacy/tests 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | input.tool-mfa-verification-code, 2 | .tool-mfa-verification-code input { 3 | /* Some elements must be important to override form element*/ 4 | font-size: 1.25em !important; 5 | letter-spacing: 1.05em; 6 | font-family: monospace; 7 | width: 11.5em !important; 8 | } -------------------------------------------------------------------------------- /templates/guide_link.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template tool_mfa/guide_link 19 | 20 | Template for the link to the user guide. 21 | 22 | Example context (json): 23 | { 24 | } 25 | }} 26 | -------------------------------------------------------------------------------- /tests/plugininfo_factor_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Tests for plugininfo. 19 | * 20 | * @package tool_mfa 21 | * @author Peter Burnett 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class plugininfo_factor_test extends advanced_testcase { 26 | 27 | public function test_get_next_user_factor() { 28 | 29 | $this->resetAfterTest(true); 30 | 31 | // Create and login a user. 32 | $user = $this->getDataGenerator()->create_user(); 33 | $this->setUser($user); 34 | 35 | // Test that with no enabled factors, fallback is returned. 36 | $this->assertEquals(\tool_mfa\plugininfo\factor::get_next_user_factor()->name, 'fallback'); 37 | 38 | // Setup enabled totp factor for user. 39 | set_config('enabled', 1, 'factor_totp'); 40 | $totpfactor = \tool_mfa\plugininfo\factor::get_factor('totp'); 41 | $totpdata = [ 42 | 'secret' => 'fakekey', 43 | 'devicename' => 'fakedevice', 44 | ]; 45 | $this->assertNotEmpty($totpfactor->setup_user_factor((object) $totpdata)); 46 | 47 | // Test that factor now appears (from STATE_UNKNOWN). 48 | $this->assertEquals(\tool_mfa\plugininfo\factor::get_next_user_factor()->name, 'totp'); 49 | 50 | // Now pass this factor, check for fallback. 51 | $totpfactor->set_state(\tool_mfa\plugininfo\factor::STATE_PASS); 52 | $this->assertEquals(\tool_mfa\plugininfo\factor::get_next_user_factor()->name, 'fallback'); 53 | 54 | // Add in a no-input factor. 55 | set_config('enabled', 1, 'factor_auth'); 56 | $this->assertEquals(2, count(\tool_mfa\plugininfo\factor::get_enabled_factors())); 57 | 58 | $authfactor = \tool_mfa\plugininfo\factor::get_factor('auth'); 59 | $this->assertTrue($authfactor->is_enabled()); 60 | $this->assertFalse($authfactor->has_setup()); 61 | 62 | // Check that the next factor is still the fallback factor. 63 | $this->assertEquals(2, count(\tool_mfa\plugininfo\factor::get_active_user_factor_types())); 64 | $this->assertEquals(\tool_mfa\plugininfo\factor::get_next_user_factor()->name, 'fallback'); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/tool_mfa_testcase.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_mfa\tests; 18 | 19 | defined('MOODLE_INTERNAL') || die(); 20 | 21 | global $CFG; 22 | require_once($CFG->libdir.'/adminlib.php'); 23 | require_once(__DIR__ . '/../lib.php'); 24 | 25 | /** 26 | * Base testcase class for testing this plugin 27 | * 28 | * @package tool_mfa 29 | * @author Mikhail Golenkov 30 | * @author Peter Burnett 31 | * @copyright Catalyst IT 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | abstract class tool_mfa_testcase extends \advanced_testcase { 35 | 36 | /** 37 | * Sets the state of the factor, in particular the weight and whether it is enabled 38 | * 39 | * @param string $factorname 40 | * @param int $enabled 41 | * @param int $weight 42 | */ 43 | protected function set_factor_state($factorname, $enabled = 0, $weight = 100) { 44 | $factor = \tool_mfa\plugininfo\factor::get_factor($factorname); 45 | $this->set_factor_config($factor, 'enabled', $enabled); 46 | $this->set_factor_config($factor, 'weight', $weight); 47 | } 48 | 49 | /** 50 | * Sets config variable for given factor. 51 | * 52 | * @param object $factor object of the factor class 53 | * @param string $key 54 | * @param mixed $value 55 | */ 56 | protected function set_factor_config($factor, $key, $value) { 57 | \tool_mfa\manager::set_factor_config([$key => $value], 'factor_' . $factor->name); 58 | 59 | if ($key == 'enabled') { 60 | if ($value == 1) { 61 | \tool_mfa\manager::do_factor_action($factor->name, 'enable'); 62 | } else { 63 | \tool_mfa\manager::do_factor_action($factor->name, 'disable'); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /thirdpartylibs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | factor/totp/extlib/Assert 5 | Assert 6 | 7 | MIT 8 | 2.1+ 9 | 10 | 11 | factor/totp/extlib/OTPHP 12 | OTPHP 13 | 14 | MIT 15 | 2.1+ 16 | 17 | 18 | factor/totp/extlib/ParagonIE 19 | ParagonIE 20 | 21 | Custom 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /user_preferences.php: -------------------------------------------------------------------------------- 1 | . 16 | /** 17 | * User preferences page 18 | * 19 | * @package tool_mfa 20 | * @author Mikhail Golenkov 21 | * @copyright Catalyst IT 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(__DIR__ . '/../../../config.php'); 26 | 27 | require_login(null, false); 28 | if (isguestuser()) { 29 | throw new require_login_exception('Guests are not allowed here.'); 30 | } 31 | 32 | $action = optional_param('action', '', PARAM_TEXT); 33 | $factorid = optional_param('factorid', 0, PARAM_INT); 34 | 35 | $context = context_user::instance($USER->id); 36 | $PAGE->set_context($context); 37 | $PAGE->set_url('/admin/tool/mfa/user_preferences.php'); 38 | $PAGE->set_pagelayout('standard'); 39 | $PAGE->set_title(get_string('preferences:header', 'tool_mfa')); 40 | $PAGE->set_cacheable(false); 41 | 42 | if ($node = $PAGE->settingsnav->find('usercurrentsettings', null)) { 43 | $PAGE->navbar->add($node->get_content(), $node->action()); 44 | } 45 | $PAGE->navbar->add(get_string('preferences:header', 'tool_mfa'), new \moodle_url('/admin/tool/mfa/user_preferences.php')); 46 | $OUTPUT = $PAGE->get_renderer('tool_mfa'); 47 | 48 | echo $OUTPUT->header(); 49 | if (!empty($action)) { 50 | if ($factorid != 0) { 51 | $instance = \tool_mfa\plugininfo\factor::get_instance_from_id($factorid); 52 | // Confirm factor is valid for the accessing user. 53 | if ($USER->id == $instance->userid) { 54 | $factor = \tool_mfa\plugininfo\factor::get_factor($instance->factor); 55 | $string = $factor->get_display_name().' - '.$instance->label; 56 | echo $OUTPUT->notification(get_string('factor'.$action, 'tool_mfa', $string), 'notifysuccess'); 57 | } 58 | } 59 | } 60 | 61 | echo $OUTPUT->active_factors(); 62 | echo $OUTPUT->available_factors(); 63 | 64 | echo $OUTPUT->guide_link(); 65 | 66 | \tool_mfa\manager::display_debug_notification(); 67 | 68 | echo $OUTPUT->footer(); 69 | 70 | if (!empty($SESSION->tool_mfa_setwantsurl) && $SESSION->tool_mfa_setwantsurl 71 | && \tool_mfa\manager::get_total_weight() >= 100) { 72 | unset($SESSION->wantsurl); 73 | } 74 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package tool_mfa 21 | * @author Mikhail Golenkov 22 | * @copyright Catalyst IT 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $plugin->version = 2023080100; // The current plugin version (Date: YYYYMMDDXX). 29 | $plugin->release = 2023080100; // Same as version. 30 | $plugin->requires = 2022041908; // Support Moodle 4.0 and higher. 31 | $plugin->component = 'tool_mfa'; 32 | $plugin->maturity = MATURITY_STABLE; 33 | $plugin->supported = [400, 402]; 34 | --------------------------------------------------------------------------------