Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace tool_mfa\plugininfo;use moodle_url;use stdClass;/*** Subplugin info class.** @package tool_mfa* @author Mikhail Golenkov <golenkovm@gmail.com>* @copyright Catalyst IT* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class factor extends \core\plugininfo\base {/** @var string */const STATE_UNKNOWN = 'unknown';/** @var string */const STATE_PASS = 'pass';/** @var string */const STATE_FAIL = 'fail';/** @var string */const STATE_NEUTRAL = 'neutral';/** @var string Locked state is identical to neutral, but can't be overridden */const STATE_LOCKED = 'locked';/*** Finds all MFA factors.** @return array of factor objects.*/public static function get_factors(): array {$return = [];$factors = \core_plugin_manager::instance()->get_plugins_of_type('factor');foreach ($factors as $factor) {$classname = '\\factor_'.$factor->name.'\\factor';if (class_exists($classname)) {$return[] = new $classname($factor->name);}}return self::sort_factors_by_order($return);}/*** Sorts factors by configured order.** @param array $unsorted of factor objects* @return array of factor objects* @throws \dml_exception*/public static function sort_factors_by_order(array $unsorted): array {$sorted = [];$orderarray = explode(',', get_config('tool_mfa', 'factor_order'));foreach ($orderarray as $order => $factorname) {foreach ($unsorted as $key => $factor) {if ($factor->name == $factorname) {$sorted[] = $factor;unset($unsorted[$key]);}}}$sorted = array_merge($sorted, $unsorted);return $sorted;}/*** Finds factor by its name.** @param string $name** @return mixed factor object or false if factor not found.*/public static function get_factor(string $name): object|bool {$factors = \core_plugin_manager::instance()->get_plugins_of_type('factor');foreach ($factors as $factor) {if ($name == $factor->name) {$classname = '\\factor_'.$factor->name.'\\factor';if (class_exists($classname)) {return new $classname($factor->name);}}}return false;}/*** Finds all enabled factors.** @return array of factor objects*/public static function get_enabled_factors(): array {$return = [];$factors = self::get_factors();foreach ($factors as $factor) {if ($factor->is_enabled()) {$return[] = $factor;}}return $return;}/*** Finds active factors for a user.* If user is not specified, current user is used.** @param mixed $user user object or null.* @return array of factor objects.*/public static function get_active_user_factor_types(mixed $user = null): array {global $USER;if (is_null($user)) {$user = $USER;}$return = [];$factors = self::get_enabled_factors();foreach ($factors as $factor) {$userfactors = $factor->get_active_user_factors($user);if (count($userfactors) > 0) {$return[] = $factor;}}return $return;}/*** Returns next factor to authenticate user.* Only returns factors that require user input.** @return mixed factor object the next factor to be authenticated or false.*/public static function get_next_user_login_factor(): mixed {$factors = self::get_active_user_factor_types();foreach ($factors as $factor) {if (!$factor->has_input()) {continue;}if ($factor->get_state() == self::STATE_UNKNOWN) {return $factor;}}return new \tool_mfa\local\factor\fallback();}/*** Returns all factors that require user input.** @return array of factor objects.*/public static function get_all_user_login_factors(): array {$factors = self::get_active_user_factor_types();$loginfactors = [];foreach ($factors as $factor) {if ($factor->has_input()) {$loginfactors[] = $factor;}}return $loginfactors;}/*** Returns the list of available actions with factor.** @return array*/public static function get_factor_actions(): array {$actions = [];$actions[] = 'setup';$actions[] = 'revoke';$actions[] = 'enable';$actions[] = 'revoke';$actions[] = 'disable';$actions[] = 'up';$actions[] = 'down';$actions[] = 'manage';$actions[] = 'replace';return $actions;}/*** Returns the information about plugin availability** True means that the plugin is enabled. False means that the plugin is* disabled. Null means that the information is not available, or the* plugin does not support configurable availability or the availability* can not be changed.** @return null|bool*/public function is_enabled(): null|bool {if (!$this->rootdir) {// Plugin missing.return false;}$factor = $this->get_factor($this->name);if ($factor) {return $factor->is_enabled();}return false;}/*** Returns section name for settings.** @return string*/public function get_settings_section_name(): string {return $this->type . '_' . $this->name;}/*** Loads factor settings to the settings tree** This function usually includes settings.php file in plugins folder.* Alternatively it can create a link to some settings page (instance of admin_externalpage)** @param \part_of_admin_tree $adminroot* @param string $parentnodename* @param bool $hassiteconfig whether the current user has moodle/site:config capability*/public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig): void {if (!$this->is_installed_and_upgraded()) {return;}if (!$hassiteconfig || !file_exists($this->full_path('settings.php'))) {return;}$section = $this->get_settings_section_name();$settings = new \admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false);if ($adminroot->fulltree) {include($this->full_path('settings.php'));}$adminroot->add($parentnodename, $settings);}/*** Checks that given factor exists.** @param string $factorname** @return bool*/public static function factor_exists(string $factorname): bool {$factor = self::get_factor($factorname);return !$factor ? false : true;}/*** Returns instance of any factor from the factorid.** @param int $factorid** @return stdClass|null Factor instance or nothing if not found.*/public static function get_instance_from_id(int $factorid): stdClass|null {global $DB;return $DB->get_record('tool_mfa', ['id' => $factorid]);}/*** Return URL used for management of plugins of this type.** @return moodle_url*/public static function get_manage_url(): moodle_url {return new moodle_url('/admin/settings.php', ['section' => 'managemfa',]);}/*** These subplugins can be uninstalled.** @return bool*/public function is_uninstall_allowed(): bool {return $this->name !== 'nosetup';}/*** Pre-uninstall hook.** This is intended for disabling of plugin, some DB table purging, etc.** NOTE: to be called from uninstall_plugin() only.* @private*/public function uninstall_cleanup() {global $DB, $CFG;$DB->delete_records('tool_mfa', ['factor' => $this->name]);$DB->delete_records('tool_mfa_secrets', ['factor' => $this->name]);$order = explode(',', get_config('tool_mfa', 'factor_order'));if (in_array($this->name, $order)) {$order = array_diff($order, [$this->name]);\tool_mfa\manager::set_factor_config(['factor_order' => implode(',', $order)], 'tool_mfa');}parent::uninstall_cleanup();}/*** Sorts factors by state.** @param array $factors The factors to sort.* @param string $state The state to sort by.* @return array $factors The sorted factors.*/public static function sort_factors_by_state(array $factors, string $state): array {usort($factors, function ($a, $b) use ($state) {$statea = $a->get_state();$stateb = $b->get_state();if ($statea === $state && $stateb !== $state) {return -1; // A comes before B.}if ($stateb === $state && $statea !== $state) {return 1; // B comes before A.}return 0; // They are the same, keep current order.});return $factors;}/*** Check if the current user has more than one active factor.** @return bool Returns true if there are more than one.*/public static function user_has_more_than_one_active_factors(): bool {$factors = self::get_active_user_factor_types();$count = count(array_filter($factors, function($factor) {// Include only user factors that can be set.return $factor->has_input();}));return $count > 1;}}