From 1357df16636c72ae9d052d68bae748f200047318 Mon Sep 17 00:00:00 2001 From: Brendan Heywood Date: Wed, 29 Jul 2026 13:42:52 +1000 Subject: [PATCH 1/2] Add readme --- README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ version.php | 2 +- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c1492f --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# moodle-factor_user + +* [What is this?](#what-is-this) +* [Branches](#branches) +* [Installation](#installation) +* [Configuration](#configuration) +* [Support](#support) + +## What is this? + +This is a factor plugin for the Moodle [MFA Plugin](https://github.com/moodle/moodle/tree/main/admin/tool/mfa) which allows specific user accounts to be exempted from MFA requirements entirely. + +Users listed in the configuration will automatically **pass** this factor, bypassing the need for any additional authentication. All other users receive a neutral result, meaning this factor does not affect them and other configured factors will determine their MFA requirement as normal. + +This is intended for **integration accounts** or **service accounts** that authenticate programmatically and cannot complete interactive MFA challenges. + +## Branches + +| Moodle version | Branch | +|---|---| +| Moodle 4.5+ | `main` | + +## Installation + +Clone or copy this plugin into your Moodle installation: + +```bash +git clone https://github.com/catalyst/moodle-factor_user admin/tool/mfa/factor/user +``` + +Then run the upgrade: + +```bash +php admin/cli/upgrade.php +``` + +Or complete the upgrade via **Site administration → Notifications**. + +## Configuration + +1. Go to **Site administration → Security → Multi-factor authentication**. +2. Enable and configure the **Exempted users** factor. +3. Set the **weight** to a value that, on its own, satisfies your MFA threshold (typically 100). +4. In the **Exempted usernames** textarea, enter one username per line for each account that should bypass MFA. + +### Example + +``` +integration_api +lms_sync_account +hr_feed_user +``` + +Any user whose username appears in this list will pass MFA automatically. Users not on the list are unaffected by this factor. + +### Recommended setup + +Combine this factor with your normal MFA factors (e.g. TOTP, Email). Ensure this factor is ordered **before** the others so exempted accounts are passed immediately without being prompted for a second factor. + +## Security issues + +> **Note:** This plugin is a workaround. Ideally, integration and service accounts should be configured with the **No login** (`nologin`) or **Web services** (`webservice`) authentication method in Moodle. Accounts using these auth methods cannot log in via the standard login form and are therefore naturally exempt from MFA without needing this plugin. Use this factor only when those auth methods are not an option for your integration. + +Granting MFA exemptions reduces security for the affected accounts. Ensure that: + +- Only non-interactive service/integration accounts are added to the exemption list. +- Exempted accounts use strong, unique passwords and have minimal permissions. + +If you find a security issue with this or any Catalyst plugin, please **do not** open a GitHub issue. Instead, responsibly disclose the issue in private via email: + +security@catalyst-au.net + +## Support + +If you have issues please log them in GitHub. + +This plugin was developed by Catalyst IT Australia: + +https://www.catalyst-au.net + +Catalyst IT diff --git a/version.php b/version.php index 9d1fb42..85b2df0 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2026072900; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2026072901; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2024100100; // Requires this Moodle version (4.5). $plugin->component = 'factor_user'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; From 22deef3edc5e39e7ecbe6590dded7c6b3d036330 Mon Sep 17 00:00:00 2001 From: Brendan Heywood Date: Wed, 29 Jul 2026 13:48:08 +1000 Subject: [PATCH 2/2] CI fixes --- classes/factor.php | 1 - classes/privacy/provider.php | 1 - settings.php | 30 ++++++++++++++++++++++-------- tests/factor_test.php | 1 - 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/classes/factor.php b/classes/factor.php index 1286f93..61e99c4 100644 --- a/classes/factor.php +++ b/classes/factor.php @@ -34,7 +34,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class factor extends object_factor_base { - /** * User factor implementation. * This factor is a singleton, return single instance. diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 46dc082..c13c761 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -27,7 +27,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements null_provider { - /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. diff --git a/settings.php b/settings.php index 8d7f601..6693da6 100644 --- a/settings.php +++ b/settings.php @@ -26,23 +26,37 @@ defined('MOODLE_INTERNAL') || die(); if ($ADMIN->fulltree) { - $settings->add(new admin_setting_heading('factor_user/description', '', - new lang_string('settings:description', 'factor_user'))); + $settings->add(new admin_setting_heading( + 'factor_user/description', + '', + new lang_string('settings:description', 'factor_user') + )); $settings->add(new admin_setting_heading('factor_user/settings', new lang_string('settings', 'moodle'), '')); - $enabled = new admin_setting_configcheckbox('factor_user/enabled', + $enabled = new admin_setting_configcheckbox( + 'factor_user/enabled', new lang_string('settings:enablefactor', 'tool_mfa'), - new lang_string('settings:enablefactor_help', 'tool_mfa'), 0); + new lang_string('settings:enablefactor_help', 'tool_mfa'), + 0 + ); $enabled->set_updatedcallback(function () { \tool_mfa\manager::do_factor_action('user', get_config('factor_user', 'enabled') ? 'enable' : 'disable'); }); $settings->add($enabled); - $settings->add(new admin_setting_configtext('factor_user/weight', + $settings->add(new admin_setting_configtext( + 'factor_user/weight', new lang_string('settings:weight', 'tool_mfa'), - new lang_string('settings:weight_help', 'tool_mfa'), 100, PARAM_INT)); + new lang_string('settings:weight_help', 'tool_mfa'), + 100, + PARAM_INT + )); - $settings->add(new admin_setting_configtextarea('factor_user/usernames', + $settings->add(new admin_setting_configtextarea( + 'factor_user/usernames', new lang_string('settings:usernames', 'factor_user'), - new lang_string('settings:usernames_help', 'factor_user'), '', PARAM_TEXT)); + new lang_string('settings:usernames_help', 'factor_user'), + '', + PARAM_TEXT + )); } diff --git a/tests/factor_test.php b/tests/factor_test.php index 5490604..3f9ce49 100644 --- a/tests/factor_test.php +++ b/tests/factor_test.php @@ -25,7 +25,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class factor_test extends \advanced_testcase { - /** * Tests that a user in the exemption list receives STATE_PASS. *