From dc25b71d8bb7ad95aea4510666385c74669316ec Mon Sep 17 00:00:00 2001 From: "[Peter Burnett]" <[peterburnett@catalyst-au.net]> Date: Fri, 2 Aug 2019 10:34:19 +1000 Subject: [PATCH] MDL-66173 login: Added form injection, validation and post action hooks --- login/change_password.php | 4 + login/change_password_form.php | 7 ++ login/forgot_password_form.php | 8 ++ login/lib.php | 185 +++++++++++++++++++++++++++++++++ login/set_password_form.php | 8 ++ login/signup.php | 3 + login/signup_form.php | 7 ++ 7 files changed, 222 insertions(+) diff --git a/login/change_password.php b/login/change_password.php index f5ce3e9ea4c..66840ebdaab 100644 --- a/login/change_password.php +++ b/login/change_password.php @@ -29,6 +29,7 @@ require_once($CFG->dirroot.'/user/lib.php'); require_once('change_password_form.php'); require_once($CFG->libdir.'/authlib.php'); require_once($CFG->dirroot.'/webservice/lib.php'); +require_once('lib.php'); $id = optional_param('id', SITEID, PARAM_INT); // current course $return = optional_param('return', 0, PARAM_BOOL); // redirect after password change @@ -133,6 +134,9 @@ if ($mform->is_cancelled()) { $strpasswordchanged = get_string('passwordchanged'); + // Plugins can perform post password change actions once data has been validated. + core_login_post_change_password_requests($data); + $fullname = fullname($USER, true); $PAGE->set_title($strpasswordchanged); diff --git a/login/change_password_form.php b/login/change_password_form.php index 9f2b77d69ad..d1c978a13d4 100644 --- a/login/change_password_form.php +++ b/login/change_password_form.php @@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/user/lib.php'); +require_once('lib.php'); class login_change_password_form extends moodleform { @@ -75,6 +76,9 @@ class login_change_password_form extends moodleform { $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); + // Hook for plugins to extend form definition. + core_login_extend_change_password_form($mform, $USER); + // buttons if (get_user_preferences('auth_forcepasswordchange')) { $this->add_action_buttons(false); @@ -89,6 +93,9 @@ class login_change_password_form extends moodleform { $errors = parent::validation($data, $files); $reason = null; + // Extend validation for any form extensions from plugins. + $errors = array_merge($errors, core_login_validate_extend_change_password_form($data, $USER)); + // ignore submitted username if (!$user = authenticate_user_login($USER->username, $data['password'], true, $reason, false)) { $errors['password'] = get_string('invalidlogin'); diff --git a/login/forgot_password_form.php b/login/forgot_password_form.php index 67a4d9f84cf..4ad2447c2b4 100644 --- a/login/forgot_password_form.php +++ b/login/forgot_password_form.php @@ -26,6 +26,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/user/lib.php'); +require_once('lib.php'); /** * Reset forgotten password form definition. @@ -46,6 +47,9 @@ class login_forgot_password_form extends moodleform { $mform = $this->_form; $mform->setDisableShortforms(true); + // Hook for plugins to extend form definition. + core_login_extend_forgot_password_form($mform); + $mform->addElement('header', 'searchbyusername', get_string('searchbyusername'), ''); $purpose = user_edit_map_field_purpose($USER->id, 'username'); @@ -74,6 +78,10 @@ class login_forgot_password_form extends moodleform { function validation($data, $files) { $errors = parent::validation($data, $files); + + // Extend validation for any form extensions from plugins. + $errors = array_merge($errors, core_login_validate_extend_forgot_password_form($data)); + $errors += core_login_validate_forgot_password_data($data); return $errors; diff --git a/login/lib.php b/login/lib.php index 42643335021..b391ea7b712 100644 --- a/login/lib.php +++ b/login/lib.php @@ -51,6 +51,9 @@ function core_login_process_password_reset_request() { } list($status, $notice, $url) = core_login_process_password_reset($username, $email); + // Plugins can perform post forgot password actions once data has been validated. + core_login_post_forgot_password_requests($data); + // Any email has now been sent. // Next display results to requesting user if settings permit. echo $OUTPUT->header(); @@ -283,6 +286,10 @@ function core_login_process_password_set($token) { $urltogo = core_login_get_return_url(); unset($SESSION->wantsurl); + + // Plugins can perform post set password actions once data has been validated. + core_login_post_set_password_requests($data, $user); + redirect($urltogo, get_string('passwordset'), 1); } } @@ -399,3 +406,181 @@ function core_login_pre_signup_requests() { } } } + +/** + * Plugins can extend forms. + */ + + /** Inject form elements into change_password_form. + * @param mform $mform the form to inject elements into. + * @param stdClass $user the user object to use for context. + */ +function core_login_extend_change_password_form($mform, $user) { + $callbacks = get_plugins_with_function('extend_change_password_form'); + foreach ($callbacks as $type => $plugins) { + foreach ($plugins as $plugin => $pluginfunction) { + $pluginfunction($mform, $user); + } + } +} + + /** Inject form elements into set_password_form. + * @param mform $mform the form to inject elements into. + * @param stdClass $user the user object to use for context. + */ +function core_login_extend_set_password_form($mform, $user) { + $callbacks = get_plugins_with_function('extend_set_password_form'); + foreach ($callbacks as $type => $plugins) { + foreach ($plugins as $plugin => $pluginfunction) { + $pluginfunction($mform, $user); + } + } +} + + /** Inject form elements into forgot_password_form. + * @param mform $mform the form to inject elements into. + */ +function core_login_extend_forgot_password_form($mform) { + $callbacks = get_plugins_with_function('extend_forgot_password_form'); + foreach ($callbacks as $type => $plugins) { + foreach ($plugins as $plugin => $pluginfunction) { + $pluginfunction($mform); + } + } +} + + /** Inject form elements into signup_form. + * @param mform $mform the form to inject elements into. + */ +function core_login_extend_signup_form($mform) { + $callbacks = get_plugins_with_function('extend_signup_form'); + foreach ($callbacks as $type => $plugins) { + foreach ($plugins as $plugin => $pluginfunction) { + $pluginfunction($mform); + } + } +} + +/** + * Plugins can add additional validation to forms. + */ + +/** Inject validation into change_password_form. + * @param array $data the data array from submitted form values. + * @param stdClass $user the user object to use for context. + * @return array $errors the updated array of errors from validation. + */ +function core_login_validate_extend_change_password_form($data, $user) { + $pluginsfunction = get_plugins_with_function('validate_extend_change_password_form'); + $errors = array(); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginerrors = $pluginfunction($data, $user); + $errors = array_merge($errors, $pluginerrors); + } + } + return $errors; +} + +/** Inject validation into set_password_form. + * @param array $data the data array from submitted form values. + * @param stdClass $user the user object to use for context. + * @return array $errors the updated array of errors from validation. + */ +function core_login_validate_extend_set_password_form($data, $user) { + $pluginsfunction = get_plugins_with_function('validate_extend_set_password_form'); + $errors = array(); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginerrors = $pluginfunction($data, $user); + $errors = array_merge($errors, $pluginerrors); + } + } + return $errors; +} + +/** Inject validation into forgot_password_form. + * @param array $data the data array from submitted form values. + * @return array $errors the updated array of errors from validation. + */ +function core_login_validate_extend_forgot_password_form($data) { + $pluginsfunction = get_plugins_with_function('validate_extend_forgot_password_form'); + $errors = array(); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginerrors = $pluginfunction($data); + $errors = array_merge($errors, $pluginerrors); + } + } + return $errors; +} + +/** Inject validation into signup_form. + * @param array $data the data array from submitted form values. + * @return array $errors the updated array of errors from validation. + */ +function core_login_validate_extend_signup_form($data) { + $pluginsfunction = get_plugins_with_function('validate_extend_signup_form'); + $errors = array(); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginerrors = $pluginfunction($data); + $errors = array_merge($errors, $pluginerrors); + } + } + return $errors; +} + +/** + * Plugins can perform post submission actions. + */ + +/** Post change_password_form submission actions. + * @param stdClass $data the data object from the submitted form. + */ +function core_login_post_change_password_requests($data) { + $pluginsfunction = get_plugins_with_function('post_change_password_requests'); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginfunction($data); + } + } +} + +/** Post set_password_form submission actions. + * @param stdClass $data the data object from the submitted form. + * @param stdClass $user the user object for set_password context. + */ +function core_login_post_set_password_requests($data, $user) { + $pluginsfunction = get_plugins_with_function('post_set_password_requests'); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginfunction($data, $user); + } + } +} + +/** Post forgot_password_form submission actions. + * @param stdClass $data the data object from the submitted form. + */ +function core_login_post_forgot_password_requests($data) { + $pluginsfunction = get_plugins_with_function('post_forgot_password_requests'); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginfunction($data); + } + } +} + +/** Post signup_form submission actions. + * @param stdClass $data the data object from the submitted form. + */ +function core_login_post_signup_requests($data) { + $pluginsfunction = get_plugins_with_function('post_signup_requests'); + foreach ($pluginsfunction as $plugintype => $plugins) { + foreach ($plugins as $pluginfunction) { + $pluginfunction($data); + } + } +} + diff --git a/login/set_password_form.php b/login/set_password_form.php index 6cee4e9ea1c..1b29fb554fa 100644 --- a/login/set_password_form.php +++ b/login/set_password_form.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/user/lib.php'); +require_once('lib.php'); /** * Set forgotten password form definition. @@ -78,6 +79,10 @@ class login_set_password_form extends moodleform { $mform->addRule('password2', get_string('required'), 'required', null, 'client'); $mform->setType('password2', PARAM_RAW); + // Hook for plugins to extend form definition. + $user = $this->_customdata; + core_login_extend_set_password_form($mform, $user); + $this->add_action_buttons(true); } @@ -92,6 +97,9 @@ class login_set_password_form extends moodleform { $errors = parent::validation($data, $files); + // Extend validation for any form extensions from plugins. + $errors = array_merge($errors, core_login_validate_extend_set_password_form($data, $user)); + // Ignore submitted username. if ($data['password'] !== $data['password2']) { $errors['password'] = get_string('passwordsdiffer'); diff --git a/login/signup.php b/login/signup.php index dd2e2ce8669..d46f4531efd 100644 --- a/login/signup.php +++ b/login/signup.php @@ -86,6 +86,9 @@ if ($mform_signup->is_cancelled()) { // Add missing required fields. $user = signup_setup_new_user($user); + // Plugins can perform post sign up actions once data has been validated. + core_login_post_signup_requests($user); + $authplugin->user_signup($user, true); // prints notice and link to login/index.php exit; //never reached } diff --git a/login/signup_form.php b/login/signup_form.php index ca975aa62b0..8dc535411e8 100644 --- a/login/signup_form.php +++ b/login/signup_form.php @@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/user/profile/lib.php'); require_once($CFG->dirroot . '/user/editlib.php'); +require_once('lib.php'); class login_signup_form extends moodleform implements renderable, templatable { function definition() { @@ -97,6 +98,9 @@ class login_signup_form extends moodleform implements renderable, templatable { $mform->closeHeaderBefore('recaptcha_element'); } + // Hook for plugins to extend form definition. + core_login_extend_signup_form($mform); + // Add "Agree to sitepolicy" controls. By default it is a link to the policy text and a checkbox but // it can be implemented differently in custom sitepolicy handlers. $manager = new \core_privacy\local\sitepolicy\manager(); @@ -128,6 +132,9 @@ class login_signup_form extends moodleform implements renderable, templatable { public function validation($data, $files) { $errors = parent::validation($data, $files); + // Extend validation for any form extensions from plugins. + $errors = array_merge($errors, core_login_validate_extend_signup_form($data)); + if (signup_captcha_enabled()) { $recaptchaelement = $this->_form->getElement('recaptcha_element'); if (!empty($this->_form->_submitValues['g-recaptcha-response'])) {