mirror of
https://github.com/PawelSuwinski/moodle-auth_emailotp.git
synced 2026-05-17 05:48:40 +02:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17c7d55cd2 | ||
|
|
ae7ab59097 | ||
|
|
53162e2a91 | ||
|
|
50699c4caa | ||
|
|
6d9b5f8300 | ||
|
|
6b67b5940d | ||
|
|
a556e3bf4c | ||
|
|
e71f991d6e | ||
|
|
fddba75efc | ||
|
|
bb4d2927fa | ||
|
|
79ca27cad8 | ||
|
|
7b6e11a288 | ||
|
|
1ef35c31bd | ||
|
|
23c5697841 | ||
|
|
d4c2f0130a | ||
|
|
a660541540 | ||
|
|
c019d32cf8 | ||
|
|
792a9e1c7e | ||
|
|
15450e8eec | ||
|
|
03680da8f0 | ||
|
|
fc56948099 | ||
|
|
2db88bb417 | ||
|
|
b19202e705 | ||
|
|
4b0e41dbf3 |
26
CHANGELOG.md
Normal file
26
CHANGELOG.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.1.2] - 2021-01-05
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- docs: misspelled fields mapping PCRE pattern example (issue #1)
|
||||||
|
|
||||||
|
|
||||||
|
## [1.1.1] - 2020-11-26
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- notice "only variables should be passed by reference"
|
||||||
|
|
||||||
|
|
||||||
|
## [1.1.0] - 2020-11-19
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- security settings revokethreshold and minrequestperiod
|
||||||
|
- support for standard not email based login of existing users
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- settings form help typos
|
||||||
23
README.md
23
README.md
@@ -1,11 +1,20 @@
|
|||||||
# Email One-Time Password Authentication
|
# Email One-Time Password Authentication
|
||||||
|
|
||||||
Matches only valid email from allowed domain as username. Validates client
|
Validates credentials and password if exists in current session or generates
|
||||||
credentials and password if exists in current session or generates ones for
|
ones for session time on empty password which is treated as one-time password
|
||||||
session time on empty password treated as one-time password request.
|
request and sends it to an email. Matches only valid email from allowed domains
|
||||||
|
using global `allowemailaddresses` and `denyemailaddresses` settings if set.
|
||||||
|
|
||||||
On first login account is created if not prevented on global level
|
Additional security can be set:
|
||||||
and parts of email address may be mapped to profile fields using
|
- *revoke threshold*: login failures limit causing revoke of the generated password
|
||||||
PCRE expressions.
|
- *minimum request period*: a time in seconds after which another password can be generated
|
||||||
|
|
||||||
See [setting form help](settings.php) for mapping usage example.
|
Signup and user creation on first login if not prevented (global setting
|
||||||
|
`authpreventaccountcreation`) takes place only in case of using email as
|
||||||
|
username (not to be confused with the `authloginviaemail` global setting) and
|
||||||
|
parts of email address may be mapped to profile fields using PCRE expressions.
|
||||||
|
|
||||||
|
Auth instruction setting (global `auth_instructions`) is recommended depending
|
||||||
|
on the adopted user account policy and plugin configuration.
|
||||||
|
|
||||||
|
See also: `fieldsmapping_help` setting form for [mapping usage example](lang/en/auth_emailotp.php).
|
||||||
|
|||||||
5
TODO.md
5
TODO.md
@@ -1,5 +1,8 @@
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
- unit tests
|
- one-time login link / form autosubmit
|
||||||
|
- configurable takeover of user authtype after self singup in other auth module
|
||||||
|
in case of standard not email based logins
|
||||||
- js snippet to change login form (ex. to add "Generate OTP" button) on valid
|
- js snippet to change login form (ex. to add "Generate OTP" button) on valid
|
||||||
email as username
|
email as username
|
||||||
|
- unit tests
|
||||||
|
|||||||
156
auth.php
156
auth.php
@@ -18,6 +18,7 @@
|
|||||||
* Email OTP authentication plugin.
|
* Email OTP authentication plugin.
|
||||||
*
|
*
|
||||||
* @see self::user_login()
|
* @see self::user_login()
|
||||||
|
* @see self::get_user_field()
|
||||||
* @package auth_emailotp
|
* @package auth_emailotp
|
||||||
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
@@ -33,6 +34,7 @@ use core\output\notification;
|
|||||||
* Email OTP authentication plugin.
|
* Email OTP authentication plugin.
|
||||||
*
|
*
|
||||||
* @see self::user_login()
|
* @see self::user_login()
|
||||||
|
* @see self::get_user_field()
|
||||||
* @package auth_emailotp
|
* @package auth_emailotp
|
||||||
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
@@ -63,9 +65,9 @@ class auth_plugin_emailotp extends auth_plugin_base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matches only valid and allowed email as username. Validates credentials
|
* Matches only valid email from allowed domains. Validates credentials and
|
||||||
* and password if exists in current session or generates ones for session
|
* password if exists in current session or generates ones for session time
|
||||||
* time on empty password treated as one-time password request.
|
* on empty password treated as one-time password request.
|
||||||
*
|
*
|
||||||
* @param string $username The username
|
* @param string $username The username
|
||||||
* @param string $password The password
|
* @param string $password The password
|
||||||
@@ -73,56 +75,64 @@ class auth_plugin_emailotp extends auth_plugin_base {
|
|||||||
*/
|
*/
|
||||||
public function user_login($username, $password) {
|
public function user_login($username, $password) {
|
||||||
global $CFG, $DB;
|
global $CFG, $DB;
|
||||||
if (!validate_email($username) || email_is_not_allowed($username)) {
|
$email = validate_email($username)
|
||||||
|
? $username // Email as username or signup on first login.
|
||||||
|
: $this->get_user_field($username, 'email'); // Standard login, existing user.
|
||||||
|
if (empty($email) || email_is_not_allowed($email)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// OTP already generated and base credentials matches.
|
// OTP already generated and base credentials matches.
|
||||||
if (isset($_SESSION[self::COMPONENT_NAME]) &&
|
if (isset($_SESSION[self::COMPONENT_NAME]) &&
|
||||||
$_SESSION[self::COMPONENT_NAME]['credentials'] === static::get_credentials($username)) {
|
$_SESSION[self::COMPONENT_NAME]['credentials'] === static::get_credentials($username)) {
|
||||||
return empty($password)
|
if (empty($password)) {
|
||||||
? (bool) $this->redirect($username, notification::NOTIFY_INFO)
|
return (bool) $this->redirect($username, 'otpsent', notification::NOTIFY_INFO);
|
||||||
: password_verify($password, $_SESSION[self::COMPONENT_NAME]['password']);
|
} else if (password_verify($password, $_SESSION[self::COMPONENT_NAME]['password'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// OTP request - do not proceed on preventaccountcreation when user not exits.
|
// OTP request - do not proceed on preventaccountcreation when user not exits.
|
||||||
if (!isset($_SESSION[self::COMPONENT_NAME]) && empty($password) && (
|
if (!isset($_SESSION[self::COMPONENT_NAME]) && empty($password) && (
|
||||||
empty($CFG->authpreventaccountcreation) || $DB->get_field('user', 'id', [
|
empty($CFG->authpreventaccountcreation) || $this->get_user_field($username, 'id'))) {
|
||||||
'username' => $username,
|
if (!$this->min_request_period_fulfilled($email)) {
|
||||||
'mnethostid' => $CFG->mnet_localhost_id,
|
$this->redirect($username, 'otpperiod', notification::NOTIFY_WARNING);
|
||||||
'auth' => $this->authtype,
|
} else if ($this->gen_otp($username, $email)) {
|
||||||
'deleted' => 0,
|
\auth_emailotp\event\otp_generated::create(array(
|
||||||
]))) {
|
'other' => array('email' => $email),
|
||||||
$this->redirect($username, $this->gen_otp($username)
|
))->trigger();
|
||||||
? notification::NOTIFY_SUCCESS
|
$this->redirect($username, 'otpsent', notification::NOTIFY_SUCCESS);
|
||||||
: notification::NOTIFY_ERROR
|
} else {
|
||||||
|
$this->redirect($username, 'otpsent', notification::NOTIFY_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OTP exits but validation failed - reset if revoke threshold is set.
|
||||||
|
if (isset($_SESSION[self::COMPONENT_NAME])) {
|
||||||
|
$_SESSION[self::COMPONENT_NAME]['login_failed_count']++;
|
||||||
|
if (!empty($this->config->revokethreshold) &&
|
||||||
|
$_SESSION[self::COMPONENT_NAME]['login_failed_count'] >= $this->config->revokethreshold) {
|
||||||
|
unset($_SESSION[self::COMPONENT_NAME]);
|
||||||
|
\core\notification::add(get_string('otprevoked', self::COMPONENT_NAME),
|
||||||
|
notification::NOTIFY_WARNING
|
||||||
);
|
);
|
||||||
|
\auth_emailotp\event\otp_revoked::create(array(
|
||||||
|
'other' => array('email' => $email),
|
||||||
|
))->trigger();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function is_synchronised_with_external() {
|
public function is_synchronised_with_external() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function is_internal() {
|
public function is_internal() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function can_be_manually_set() {
|
public function can_be_manually_set() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function user_authenticated_hook(&$user, $username, $password) {
|
public function user_authenticated_hook(&$user, $username, $password) {
|
||||||
// Destroy credentials - is already used.
|
// Destroy credentials - is already used.
|
||||||
if (isset($_SESSION[self::COMPONENT_NAME])) {
|
if (isset($_SESSION[self::COMPONENT_NAME])) {
|
||||||
@@ -131,7 +141,13 @@ class auth_plugin_emailotp extends auth_plugin_base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* get_userinfo
|
||||||
|
*
|
||||||
|
* Signup and user creation on first login takes place only in case of
|
||||||
|
* using email address as username.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function get_userinfo($username) {
|
public function get_userinfo($username) {
|
||||||
$this->get_custom_user_profile_fields();
|
$this->get_custom_user_profile_fields();
|
||||||
@@ -173,33 +189,28 @@ class auth_plugin_emailotp extends auth_plugin_base {
|
|||||||
* gen_otp
|
* gen_otp
|
||||||
*
|
*
|
||||||
* @param string $username
|
* @param string $username
|
||||||
|
* @param string $email
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function gen_otp(string $username) {
|
protected function gen_otp(string $username, string $email) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
$newpassword = generate_password();
|
$newpassword = generate_password();
|
||||||
$_SESSION[self::COMPONENT_NAME] = array(
|
$_SESSION[self::COMPONENT_NAME] = array(
|
||||||
'credentials' => static::get_credentials($username),
|
'credentials' => static::get_credentials($username),
|
||||||
'password' => password_hash($newpassword, PASSWORD_DEFAULT),
|
'password' => password_hash($newpassword, PASSWORD_DEFAULT),
|
||||||
|
'login_failed_count' => 0,
|
||||||
);
|
);
|
||||||
$a = (object)array(
|
$user = (object)array(
|
||||||
'username' => $username,
|
'id' => -1, // Fake due email_to_user() requirements.
|
||||||
'password' => $newpassword,
|
|
||||||
);
|
|
||||||
return email_to_user(
|
|
||||||
(object)array(
|
|
||||||
'id' => -1,
|
|
||||||
'auth' => $this->authtype,
|
'auth' => $this->authtype,
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'email' => $username,
|
'email' => $email,
|
||||||
),
|
'password' => $newpassword,
|
||||||
core_user::get_support_user(),
|
);
|
||||||
sprintf(
|
return email_to_user($user, core_user::get_support_user(),
|
||||||
'%s: %s',
|
format_string(get_site()->fullname).': '.
|
||||||
format_string(get_site()->fullname),
|
get_string('otpgeneratedsubj', self::COMPONENT_NAME, $user),
|
||||||
(string)new lang_string('otpgeneratedsubj', self::COMPONENT_NAME, $a, $CFG->lang)
|
get_string('otpgeneratedtext', self::COMPONENT_NAME, $user)
|
||||||
),
|
|
||||||
(string)new lang_string('otpgeneratedtext', self::COMPONENT_NAME, $a, $CFG->lang)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +221,52 @@ class auth_plugin_emailotp extends auth_plugin_base {
|
|||||||
* @param string $msg
|
* @param string $msg
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function redirect(string $username, string $msg) {
|
protected function redirect(string $username, string $msg, string $level) {
|
||||||
global $CFG;
|
global $CFG;
|
||||||
redirect(
|
redirect(get_login_url().'?username='.urlencode($username),
|
||||||
get_login_url().'?username='.urlencode($username),
|
get_string($msg.$level, self::COMPONENT_NAME), null, $level);
|
||||||
(string)new lang_string('otpsent'.$msg, self::COMPONENT_NAME, null, $CFG->lang),
|
}
|
||||||
null,
|
|
||||||
$msg
|
/**
|
||||||
);
|
* min_request_period_fulfilled
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function min_request_period_fulfilled(string $email) {
|
||||||
|
// Min request period security disabled.
|
||||||
|
if (empty($this->config->minrequestperiod)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Log reader required - silently return failure on absence.
|
||||||
|
$readers = get_log_manager()->get_readers('\core\log\sql_reader');
|
||||||
|
$reader = reset($readers);
|
||||||
|
return $reader && $reader->get_events_select_count(
|
||||||
|
'component = ? AND action = ? AND timecreated >= ? AND other = ?',
|
||||||
|
array(
|
||||||
|
self::COMPONENT_NAME,
|
||||||
|
'generated',
|
||||||
|
time() - $this->config->minrequestperiod,
|
||||||
|
json_encode(['email' => $email]),
|
||||||
|
)
|
||||||
|
) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_user_field
|
||||||
|
*
|
||||||
|
* @see moodle_database::get_field()
|
||||||
|
* @param string $username
|
||||||
|
* @param string $field
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function get_user_field(string $username, string $field) {
|
||||||
|
global $CFG, $DB;
|
||||||
|
return $DB->get_field('user', $field, array(
|
||||||
|
'username' => $username,
|
||||||
|
'mnethostid' => $CFG->mnet_localhost_id,
|
||||||
|
'auth' => $this->authtype,
|
||||||
|
'deleted' => 0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
classes/event/otp_generated.php
Normal file
76
classes/event/otp_generated.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when one-time password is generated.
|
||||||
|
*
|
||||||
|
* @package auth_emailotp
|
||||||
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace auth_emailotp\event;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when one-time password is generated.
|
||||||
|
*
|
||||||
|
* @package auth_emailotp
|
||||||
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class otp_generated extends \core\event\base {
|
||||||
|
|
||||||
|
protected const CRUD = 'c';
|
||||||
|
|
||||||
|
protected function init() {
|
||||||
|
$this->data['crud'] = static::CRUD;
|
||||||
|
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||||
|
$this->context = \context_system::instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_name() {
|
||||||
|
return get_string('eventotp'.substr(static::class, strrpos(static::class, '_') + 1),
|
||||||
|
'auth_emailotp');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_description() {
|
||||||
|
return sprintf('Password %s for \'%s\'', $this->action,
|
||||||
|
$this->other['email']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_legacy_logdata() {
|
||||||
|
return array(SITEID, 'auth_emailotp', $this->action, '',
|
||||||
|
$this->other['email']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom validation.
|
||||||
|
*
|
||||||
|
* @throws \coding_exception
|
||||||
|
*/
|
||||||
|
protected function validate_data() {
|
||||||
|
parent::validate_data();
|
||||||
|
if (!isset($this->other['email'])) {
|
||||||
|
throw new \coding_exception('The \'email\' value must be set in other.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_other_mapping() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
classes/event/otp_revoked.php
Normal file
38
classes/event/otp_revoked.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when one-time password is revoked.
|
||||||
|
*
|
||||||
|
* @package auth_emailotp
|
||||||
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace auth_emailotp\event;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when one-time password is revoked.
|
||||||
|
*
|
||||||
|
* @package auth_emailotp
|
||||||
|
* @copyright 2020 Pawel Suwinski <psuw@wp.pl>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class otp_revoked extends otp_generated {
|
||||||
|
protected const CRUD = 'd';
|
||||||
|
}
|
||||||
@@ -23,14 +23,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$string['pluginname'] = 'Email OTP';
|
$string['pluginname'] = 'Email OTP';
|
||||||
|
$string['eventotpgenerated'] = 'Password generated';
|
||||||
|
$string['eventotprevoked'] = 'Password revoked';
|
||||||
$string['otpgeneratedsubj'] = 'One-time password';
|
$string['otpgeneratedsubj'] = 'One-time password';
|
||||||
$string['otpgeneratedtext'] = 'One-time password for current session: {$a->password}';
|
$string['otpgeneratedtext'] = 'One-time password for current session: {$a->password}';
|
||||||
$string['otpsentsuccess'] = 'One-time password was sent to given email.';
|
$string['otpsentsuccess'] = 'One-time password was sent to given email.';
|
||||||
$string['otpsenterror'] = 'An error occurred while sending one-time password.';
|
$string['otpsenterror'] = 'An error occurred while sending one-time password.';
|
||||||
$string['otpsentinfo'] = 'One-time password for current session was already generated and sent to email.';
|
$string['otpsentinfo'] = 'One-time password for current session was already generated and sent to email.';
|
||||||
$string['fieldsmapping'] = 'User profile fields mapping';
|
$string['otprevoked'] = 'Previously generated password has been revoked due to exceeding the login failure threshold.';
|
||||||
|
$string['otpperiodwarning'] = 'Minimum period after which another password can be generated not preserved. Try again later.';
|
||||||
|
$string['revokethreshold'] = 'Revoke threshold';
|
||||||
|
$string['revokethreshold_help'] = 'Login failures limit causing revoke of the generated password (0 - unlimited).';
|
||||||
|
$string['minrequestperiod'] = 'Minium period';
|
||||||
|
$string['minrequestperiod_help'] = 'A time in seconds after which another password can be generated (0 - unrestricted). Enabled logstore required.';
|
||||||
|
$string['logstorerequired'] = '<b>Notice: no working logstore! <a href="{$a}">Enable logstore</a> or set time to 0.</b>';
|
||||||
|
$string['fieldsmapping'] = 'User profile fields mapping on signup';
|
||||||
$string['fieldsmapping_pattern'] = 'Pattern';
|
$string['fieldsmapping_pattern'] = 'Pattern';
|
||||||
$string['fieldsmapping_pattern_help'] = 'Capturing groups PCRE patttern.';
|
$string['fieldsmapping_pattern_help'] = 'Capturing groups PCRE pattern.';
|
||||||
$string['fieldsmapping_mapping'] = 'Mapping';
|
$string['fieldsmapping_mapping'] = 'Mapping';
|
||||||
$string['fieldsmapping_mapping_help'] = 'Mapping expressions.';
|
$string['fieldsmapping_mapping_help'] = 'Mapping expressions.';
|
||||||
$string['fieldsmapping_help'] = <<<'EOT'
|
$string['fieldsmapping_help'] = <<<'EOT'
|
||||||
@@ -38,7 +47,7 @@ $string['fieldsmapping_help'] = <<<'EOT'
|
|||||||
|
|
||||||
Pattern:<br />
|
Pattern:<br />
|
||||||
<pre>
|
<pre>
|
||||||
'#/?P<FIRST>[^\.]+)\.(?P<LAST>[^@]+)@(?P<COMPANY>[^\.]+).*#',
|
#/(?P<FIRST>[^\.]+)\.(?P<LAST>[^@]+)@(?P<COMPANY>[^\.]+).*#
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Mapping:<br />
|
Mapping:<br />
|
||||||
|
|||||||
@@ -23,12 +23,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$string['pluginname'] = 'Email OTP';
|
$string['pluginname'] = 'Email OTP';
|
||||||
|
$string['eventotpgenerated'] = 'Hasło wynegerowano';
|
||||||
|
$string['eventotprevoked'] = 'Hasło unieważniono';
|
||||||
$string['otpgeneratedsubj'] = 'Hasło jednorazowe';
|
$string['otpgeneratedsubj'] = 'Hasło jednorazowe';
|
||||||
$string['otpgeneratedtext'] = 'Hasło jednorazowe dla bieżącej sesji: {$a->password}';
|
$string['otpgeneratedtext'] = 'Hasło jednorazowe dla bieżącej sesji: {$a->password}';
|
||||||
$string['otpsentsuccess'] = 'Hasło jednorazowe zostało wysłane na podany adres email.';
|
$string['otpsentsuccess'] = 'Hasło jednorazowe zostało wysłane na podany adres email.';
|
||||||
$string['otpsenterror'] = 'Wystąpił błąd podczas wysyłania hasła jednorazowego.';
|
$string['otpsenterror'] = 'Wystąpił błąd podczas wysyłania hasła jednorazowego.';
|
||||||
$string['otpsentinfo'] = 'Hasło jednorazowe dla bieżącej sesji już zostało wygenerowane i wyłane.';
|
$string['otpsentinfo'] = 'Hasło jednorazowe dla bieżącej sesji już zostało wygenerowane i wysłane.';
|
||||||
$string['fieldsmapping'] = 'Mapowanie pól profilu użytkownika';
|
$string['otprevoked'] = 'Poprzednio wygenerowane hasło zostało unieważnione z powodu przekroczenia limitu niepoprawnych logowań.';
|
||||||
|
$string['otpperiodwarning'] = 'Nie zachowany minimalny odstęp, po którym kolejne hasło może być wygenerowane. Spróbuj ponownie później.';
|
||||||
|
$string['revokethreshold'] = 'Próg nieważnienia';
|
||||||
|
$string['revokethreshold_help'] = 'Limit nieudanych logowań unieważniających wygenerowane hasło (0 - bez limitu).';
|
||||||
|
$string['minrequestperiod'] = 'Minimalny odstęp';
|
||||||
|
$string['minrequestperiod_help'] = 'Czas w sekundach, po którym kolejne hasło może być wygenerowane (0 - nieograniczony). Wymaga działającego loggera.';
|
||||||
|
$string['logstorerequired'] = '<b>Uwaga: logger nieaktywny! <a href="{$a}">Aktywuj logger</a> albo ustaw czas na 0.</b>';
|
||||||
|
$string['fieldsmapping'] = 'Mapowanie pól profilu użytkownika podczas rejestracji';
|
||||||
$string['fieldsmapping_pattern'] = 'Wzorzec';
|
$string['fieldsmapping_pattern'] = 'Wzorzec';
|
||||||
$string['fieldsmapping_pattern_help'] = 'Grupujące wyrażenie regularne PCRE.';
|
$string['fieldsmapping_pattern_help'] = 'Grupujące wyrażenie regularne PCRE.';
|
||||||
$string['fieldsmapping_mapping'] = 'Mapowanie';
|
$string['fieldsmapping_mapping'] = 'Mapowanie';
|
||||||
@@ -38,7 +47,7 @@ $string['fieldsmapping_help'] = <<<'EOT'
|
|||||||
|
|
||||||
Wzorzec:<br />
|
Wzorzec:<br />
|
||||||
<pre>
|
<pre>
|
||||||
'#/?P<FIRST>[^\.]+)\.(?P<LAST>[^@]+)@(?P<COMPANY>[^\.]+).*#',
|
#/(?P<FIRST>[^\.]+)\.(?P<LAST>[^@]+)@(?P<COMPANY>[^\.]+).*#
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Mapowanie:<br />
|
Mapowanie:<br />
|
||||||
|
|||||||
24
settings.php
24
settings.php
@@ -52,6 +52,30 @@ if ($ADMIN->fulltree) {
|
|||||||
get_string('fieldsmapping_mapping', 'auth_emailotp'),
|
get_string('fieldsmapping_mapping', 'auth_emailotp'),
|
||||||
get_string('fieldsmapping_mapping_help', 'auth_emailotp'), '', PARAM_RAW_TRIMMED));
|
get_string('fieldsmapping_mapping_help', 'auth_emailotp'), '', PARAM_RAW_TRIMMED));
|
||||||
|
|
||||||
|
$settings->add(new admin_setting_heading('auth_emailotp/security',
|
||||||
|
new lang_string('security', 'admin'), ''));
|
||||||
|
|
||||||
|
$settings->add(new admin_setting_configtext('auth_emailotp/revokethreshold',
|
||||||
|
get_string('revokethreshold', 'auth_emailotp'),
|
||||||
|
get_string('revokethreshold_help', 'auth_emailotp'), 3, PARAM_INT));
|
||||||
|
|
||||||
|
$settings->add(new class(
|
||||||
|
'auth_emailotp/minrequestperiod',
|
||||||
|
get_string('minrequestperiod', 'auth_emailotp'),
|
||||||
|
get_string('minrequestperiod_help', 'auth_emailotp')
|
||||||
|
) extends admin_setting_configtext {
|
||||||
|
public function __construct($name, $visiblename, $description) {
|
||||||
|
$readers = get_log_manager()->get_readers('\core\log\sql_reader');
|
||||||
|
$logreader = reset($readers);
|
||||||
|
parent::__construct($name, $visiblename, $description, $logreader ? 120 : 0, PARAM_INT);
|
||||||
|
if (!$logreader && !empty($this->get_setting())) {
|
||||||
|
$this->description .= ' '.get_string('logstorerequired', 'auth_emailotp',
|
||||||
|
(string)new moodle_url('/admin/settings.php', ['section' => 'managelogging'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Display locking / mapping of profile fields.
|
// Display locking / mapping of profile fields.
|
||||||
$authplugin = get_auth_plugin('emailotp');
|
$authplugin = get_auth_plugin('emailotp');
|
||||||
display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields,
|
display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields,
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->version = 2020111004; // The current plugin version (Date: YYYYMMDDXX).
|
$plugin->version = 2021010500; // The current plugin version (Date: YYYYMMDDXX).
|
||||||
$plugin->requires = 2018120304; // Requires this Moodle version.
|
$plugin->requires = 2018120304; // Requires this Moodle version.
|
||||||
$plugin->component = 'auth_emailotp'; // Full name of the plugin (used for diagnostics).
|
$plugin->component = 'auth_emailotp'; // Full name of the plugin (used for diagnostics).
|
||||||
$plugin->maturity = MATURITY_STABLE;
|
$plugin->maturity = MATURITY_STABLE;
|
||||||
$plugin->release = '1.0.2';
|
$plugin->release = '1.1.2';
|
||||||
|
|||||||
Reference in New Issue
Block a user