public function test_create_user() { global $DB, $CFG; require_once $CFG->dirroot . '/user/lib.php'; $this->resetAfterTest(true); $generator = $this->getDataGenerator(); $count = $DB->count_records('user'); $this->setCurrentTimeStart(); $user = $generator->create_user(); $this->assertEquals($count + 1, $DB->count_records('user')); $this->assertSame($user->username, core_user::clean_field($user->username, 'username')); $this->assertSame($user->email, core_user::clean_field($user->email, 'email')); $this->assertSame(AUTH_PASSWORD_NOT_CACHED, $user->password); $this->assertNotEmpty($user->firstnamephonetic); $this->assertNotEmpty($user->lastnamephonetic); $this->assertNotEmpty($user->alternatename); $this->assertNotEmpty($user->middlename); $this->assertSame('manual', $user->auth); $this->assertSame('en', $user->lang); $this->assertSame('1', $user->confirmed); $this->assertSame('0', $user->deleted); $this->assertTimeCurrent($user->timecreated); $this->assertSame($user->timecreated, $user->timemodified); $this->assertSame('0.0.0.0', $user->lastip); $record = array('auth' => 'email', 'firstname' => 'Žluťoučký', 'lastname' => 'Koníček', 'firstnamephonetic' => 'Zhlutyoucky', 'lastnamephonetic' => 'Koniiczek', 'middlename' => 'Hopper', 'alternatename' => 'horse', 'idnumber' => 'abc1', 'mnethostid' => (string) $CFG->mnet_localhost_id, 'username' => 'konic666', 'password' => 'password1', 'email' => '*****@*****.**', 'confirmed' => '1', 'lang' => 'cs', 'maildisplay' => '1', 'mailformat' => '0', 'maildigest' => '1', 'autosubscribe' => '0', 'trackforums' => '0', 'deleted' => '0', 'timecreated' => '666'); $user = $generator->create_user($record); $this->assertEquals($count + 2, $DB->count_records('user')); foreach ($record as $k => $v) { if ($k === 'password') { $this->assertTrue(password_verify($v, $user->password)); } else { $this->assertSame($v, $user->{$k}); } } $record = array('firstname' => 'Some', 'lastname' => 'User', 'idnumber' => 'def', 'username' => 'user666', 'email' => '*****@*****.**', 'deleted' => '1'); $user = $generator->create_user($record); $this->assertEquals($count + 3, $DB->count_records('user')); $this->assertSame('', $user->idnumber); $this->assertSame(md5($record['username']), $user->email); $this->assertFalse(context_user::instance($user->id, IGNORE_MISSING)); // Test generating user with interests. $user = $generator->create_user(array('interests' => 'Cats, Dogs')); $userdetails = user_get_user_details($user); $this->assertSame('Cats, Dogs', $userdetails['interests']); }
/** * Performs the synchronisation of members. * * @return bool|void */ public function execute() { global $CFG, $DB; require_once $CFG->dirroot . '/enrol/lti/ims-blti/OAuth.php'; require_once $CFG->dirroot . '/enrol/lti/ims-blti/OAuthBody.php'; // Check if the authentication plugin is disabled. if (!is_enabled_auth('lti')) { mtrace('Skipping task - ' . get_string('pluginnotenabled', 'auth', get_string('pluginname', 'auth_lti'))); return true; } // Check if the enrolment plugin is disabled - isn't really necessary as the task should not run if // the plugin is disabled, but there is no harm in making sure core hasn't done something wrong. if (!enrol_is_enabled('lti')) { mtrace('Skipping task - ' . get_string('enrolisdisabled', 'enrol_lti')); return true; } // Get all the enabled tools. if ($tools = \enrol_lti\helper::get_lti_tools(array('status' => ENROL_INSTANCE_ENABLED, 'membersync' => 1))) { $ltiplugin = enrol_get_plugin('lti'); $consumers = array(); $currentusers = array(); $userphotos = array(); foreach ($tools as $tool) { mtrace("Starting - Member sync for shared tool '{$tool->id}' for the course '{$tool->courseid}'."); // Variables to keep track of information to display later. $usercount = 0; $enrolcount = 0; $unenrolcount = 0; // We check for all the users - users can access the same tool from different consumers. if ($ltiusers = $DB->get_records('enrol_lti_users', array('toolid' => $tool->id), 'lastaccess DESC')) { foreach ($ltiusers as $ltiuser) { $mtracecontent = "for the user '{$ltiuser->userid}' in the tool '{$tool->id}' for the course " . "'{$tool->courseid}'"; $usercount++; // Check if we do not have a membershipsurl - this can happen if the sync process has an unexpected error. if (!$ltiuser->membershipsurl) { mtrace("Skipping - Empty membershipsurl {$mtracecontent}."); continue; } // Check if we do not have a membershipsid - this can happen if the sync process has an unexpected error. if (!$ltiuser->membershipsid) { mtrace("Skipping - Empty membershipsid {$mtracecontent}."); continue; } $consumer = sha1($ltiuser->membershipsurl . ':' . $ltiuser->membershipsid . ':' . $ltiuser->consumerkey . ':' . $ltiuser->consumersecret); if (in_array($consumer, $consumers)) { // We have already synchronised with this consumer. continue; } $consumers[] = $consumer; $params = array('lti_message_type' => self::LTI_MESSAGE_TYPE, 'id' => $ltiuser->membershipsid, 'lti_version' => self::LTI_VERSION); mtrace("Calling memberships url '{$ltiuser->membershipsurl}' with body '" . json_encode($params) . "'"); try { $response = sendOAuthParamsPOST('POST', $ltiuser->membershipsurl, $ltiuser->consumerkey, $ltiuser->consumersecret, 'application/x-www-form-urlencoded', $params); } catch (\Exception $e) { mtrace("Skipping - No response received {$mtracecontent} from '{$ltiuser->membershipsurl}'"); mtrace($e->getMessage()); continue; } // Check the response from the consumer. $data = new \SimpleXMLElement($response); // Check if we did not receive a valid response. if (empty($data->statusinfo)) { mtrace("Skipping - Bad response received {$mtracecontent} from '{$ltiuser->membershipsurl}'"); mtrace('Skipping - Error parsing the XML received \'' . substr($response, 0, 125) . '\' ... (Displaying only 125 chars)'); continue; } // Check if we did not receive a valid response. if (strpos(strtolower($data->statusinfo->codemajor), 'success') === false) { mtrace('Skipping - Error received from the remote system: ' . $data->statusinfo->codemajor . ' ' . $data->statusinfo->severity . ' ' . $data->statusinfo->codeminor); continue; } $members = $data->memberships->member; mtrace(count($members) . ' members received.'); foreach ($members as $member) { // Set the user data. $user = new \stdClass(); $user->username = \enrol_lti\helper::create_username($ltiuser->consumerkey, $member->user_id); $user->firstname = \core_user::clean_field($member->person_name_given, 'firstname'); $user->lastname = \core_user::clean_field($member->person_name_family, 'lastname'); $user->email = \core_user::clean_field($member->person_contact_email_primary, 'email'); // Get the user data from the LTI consumer. $user = \enrol_lti\helper::assign_user_tool_data($tool, $user); if (!($dbuser = $DB->get_record('user', array('username' => $user->username, 'deleted' => 0)))) { if ($tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_ENROL_AND_UNENROL || $tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_ENROL_NEW) { // If the email was stripped/not set then fill it with a default one. This // stops the user from being redirected to edit their profile page. if (empty($user->email)) { $user->email = $user->username . "@example.com"; } $user->auth = 'lti'; $user->id = user_create_user($user); // Add the information to the necessary arrays. $currentusers[] = $user->id; $userphotos[$user->id] = $member->user_image; } } else { // If email is empty remove it, so we don't update the user with an empty email. if (empty($user->email)) { unset($user->email); } $user->id = $dbuser->id; user_update_user($user); // Add the information to the necessary arrays. $currentusers[] = $user->id; $userphotos[$user->id] = $member->user_image; } if ($tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_ENROL_AND_UNENROL || $tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_ENROL_NEW) { // Enrol the user in the course. \enrol_lti\helper::enrol_user($tool, $user->id); } } } // Now we check if we have to unenrol users who were not listed. if ($tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_ENROL_AND_UNENROL || $tool->membersyncmode == \enrol_lti\helper::MEMBER_SYNC_UNENROL_MISSING) { // Go through the users and check if any were never listed, if so, remove them. foreach ($ltiusers as $ltiuser) { if (!in_array($ltiuser->userid, $currentusers)) { $instance = new \stdClass(); $instance->id = $tool->enrolid; $instance->courseid = $tool->courseid; $instance->enrol = 'lti'; $ltiplugin->unenrol_user($instance, $ltiuser->id); } } } } mtrace("Completed - Synced members for tool '{$tool->id}' in the course '{$tool->courseid}'. " . "Processed {$usercount} users; enrolled {$enrolcount} members; unenrolled {$unenrolcount} members."); mtrace(""); } // Sync the user profile photos. mtrace("Started - Syncing user profile images."); $counter = 0; if (!empty($userphotos)) { foreach ($userphotos as $userid => $url) { if ($url) { $result = \enrol_lti\helper::update_user_profile_image($userid, $url); if ($result === \enrol_lti\helper::PROFILE_IMAGE_UPDATE_SUCCESSFUL) { $counter++; mtrace("Profile image succesfully downloaded and created for user '{$userid}' from {$url}."); } else { mtrace($result); } } } } mtrace("Completed - Synced {$counter} profile images."); } }
// Before we do anything check that the context is valid. $context = context::instance_by_id($tool->contextid); // Set the user data. $user = new stdClass(); $user->username = \enrol_lti\helper::create_username($ltirequest->info['oauth_consumer_key'], $ltirequest->info['user_id']); if (!empty($ltirequest->info['lis_person_name_given'])) { $user->firstname = $ltirequest->info['lis_person_name_given']; } else { $user->firstname = $ltirequest->info['user_id']; } if (!empty($ltirequest->info['lis_person_name_family'])) { $user->lastname = $ltirequest->info['lis_person_name_family']; } else { $user->lastname = $ltirequest->info['context_id']; } $user->email = \core_user::clean_field($ltirequest->getUserEmail(), 'email'); // Get the user data from the LTI consumer. $user = \enrol_lti\helper::assign_user_tool_data($tool, $user); // Check if the user exists. if (!($dbuser = $DB->get_record('user', array('username' => $user->username, 'deleted' => 0)))) { // If the email was stripped/not set then fill it with a default one. This // stops the user from being redirected to edit their profile page. if (empty($user->email)) { $user->email = $user->username . "@example.com"; } $user->auth = 'lti'; $user->id = user_create_user($user); // Get the updated user record. $user = $DB->get_record('user', array('id' => $user->id)); } else { if (\enrol_lti\helper::user_match($user, $dbuser)) {
/** * Test clean_field() method. */ public function test_clean_field() { // Create a 'malicious' user object/ $user = new stdClass(); $user->firstname = 'John <script>alert(1)</script> Doe'; $user->username = '******'; $user->email = ' john@testing.com '; $user->deleted = 'no'; $user->description = '<b>A description <script>alert(123);</script>about myself.</b>'; $user->userfullname = 'John Doe'; // Expected results. $this->assertEquals('John alert(1) Doe', core_user::clean_field($user->firstname, 'firstname')); $this->assertEquals('john_doe', core_user::clean_field($user->username, 'username')); $this->assertEquals('*****@*****.**', core_user::clean_field($user->email, 'email')); $this->assertEquals(0, core_user::clean_field($user->deleted, 'deleted')); $this->assertEquals('<b>A description <script>alert(123);</script>about myself.</b>', core_user::clean_field($user->description, 'description')); // Try to clean an invalid property (fullname). core_user::clean_field($user->userfullname, 'fullname'); $this->assertDebuggingCalled("The property 'fullname' could not be cleaned."); }
/** * Validate the form data. * @param array $usernew * @param array $files * @return array|bool */ public function validation($usernew, $files) { global $CFG, $DB; $usernew = (object) $usernew; $usernew->username = trim($usernew->username); $user = $DB->get_record('user', array('id' => $usernew->id)); $err = array(); if (!$user and !empty($usernew->createpassword)) { if ($usernew->suspended) { // Show some error because we can not mail suspended users. $err['suspended'] = get_string('error'); } } else { if (!empty($usernew->newpassword)) { $errmsg = ''; // Prevent eclipse warning. if (!check_password_policy($usernew->newpassword, $errmsg)) { $err['newpassword'] = $errmsg; } } else { if (!$user) { $auth = get_auth_plugin($usernew->auth); if ($auth->is_internal()) { // Internal accounts require password! $err['newpassword'] = get_string('required'); } } } } if (empty($usernew->username)) { // Might be only whitespace. $err['username'] = get_string('required'); } else { if (!$user or $user->username !== $usernew->username) { // Check new username does not exist. if ($DB->record_exists('user', array('username' => $usernew->username, 'mnethostid' => $CFG->mnet_localhost_id))) { $err['username'] = get_string('usernameexists'); } // Check allowed characters. if ($usernew->username !== core_text::strtolower($usernew->username)) { $err['username'] = get_string('usernamelowercase'); } else { if ($usernew->username !== core_user::clean_field($usernew->username, 'username')) { $err['username'] = get_string('invalidusername'); } } } } if (!$user or isset($usernew->email) && $user->email !== $usernew->email) { if (!validate_email($usernew->email)) { $err['email'] = get_string('invalidemail'); } else { if (empty($CFG->allowaccountssameemail) and $DB->record_exists('user', array('email' => $usernew->email, 'mnethostid' => $CFG->mnet_localhost_id))) { $err['email'] = get_string('emailexists'); } } } // Next the customisable profile fields. $err += profile_validation($usernew, $files); if (count($err) == 0) { return true; } else { return $err; } }
$data = array(); $cir->init(); $linenum = 1; //column header is first line $noerror = true; // Keep status of any error. while ($linenum <= $previewrows and $fields = $cir->next()) { $linenum++; $rowcols = array(); $rowcols['line'] = $linenum; foreach ($fields as $key => $field) { $rowcols[$filecolumns[$key]] = s(trim($field)); } $rowcols['status'] = array(); if (isset($rowcols['username'])) { $stdusername = core_user::clean_field($rowcols['username'], 'username'); if ($rowcols['username'] !== $stdusername) { $rowcols['status'][] = get_string('invalidusernameupload'); } if ($userid = $DB->get_field('user', 'id', array('username' => $stdusername, 'mnethostid' => $CFG->mnet_localhost_id))) { $rowcols['username'] = html_writer::link(new moodle_url('/user/profile.php', array('id' => $userid)), $rowcols['username']); } } else { $rowcols['status'][] = get_string('missingusername'); } if (isset($rowcols['email'])) { if (!validate_email($rowcols['email'])) { $rowcols['status'][] = get_string('invalidemail'); } $select = $DB->sql_like('email', ':email', false, true, false, '|'); $params = array('email' => $DB->sql_like_escape($rowcols['email'], '|'));
function validation($data, $files) { global $CFG, $DB; $errors = parent::validation($data, $files); $authplugin = get_auth_plugin($CFG->registerauth); if ($DB->record_exists('user', array('username' => $data['username'], 'mnethostid' => $CFG->mnet_localhost_id))) { $errors['username'] = get_string('usernameexists'); } else { //check allowed characters if ($data['username'] !== core_text::strtolower($data['username'])) { $errors['username'] = get_string('usernamelowercase'); } else { if ($data['username'] !== core_user::clean_field($data['username'], 'username')) { $errors['username'] = get_string('invalidusername'); } } } //check if user exists in external db //TODO: maybe we should check all enabled plugins instead if ($authplugin->user_exists($data['username'])) { $errors['username'] = get_string('usernameexists'); } if (!validate_email($data['email'])) { $errors['email'] = get_string('invalidemail'); } else { if ($DB->record_exists('user', array('email' => $data['email']))) { $errors['email'] = get_string('emailexists') . ' <a href="forgot_password.php">' . get_string('newpassword') . '?</a>'; } } if (empty($data['email2'])) { $errors['email2'] = get_string('missingemail'); } else { if ($data['email2'] != $data['email']) { $errors['email2'] = get_string('invalidemail'); } } if (!isset($errors['email'])) { if ($err = email_is_not_allowed($data['email'])) { $errors['email'] = $err; } } $errmsg = ''; if (!check_password_policy($data['password'], $errmsg)) { $errors['password'] = $errmsg; } if ($this->signup_captcha_enabled()) { $recaptcha_element = $this->_form->getElement('recaptcha_element'); if (!empty($this->_form->_submitValues['recaptcha_challenge_field'])) { $challenge_field = $this->_form->_submitValues['recaptcha_challenge_field']; $response_field = $this->_form->_submitValues['recaptcha_response_field']; if (true !== ($result = $recaptcha_element->verify($challenge_field, $response_field))) { $errors['recaptcha'] = $result; } } else { $errors['recaptcha'] = get_string('missingrecaptchachallengefield'); } } // Validate customisable profile fields. (profile_validation expects an object as the parameter with userid set) $dataobject = (object) $data; $dataobject->id = 0; $errors += profile_validation($dataobject, $files); return $errors; }
/** * Clean the user data. * * @param stdClass|array $user the user data to be validated against properties definition. * @return stdClass $user the cleaned user data. */ public static function clean_data($user) { if (empty($user)) { return $user; } foreach ($user as $field => $value) { // Get the property parameter type and do the cleaning. try { $user->{$field} = core_user::clean_field($value, $field); } catch (coding_exception $e) { debugging("The property '{$field}' could not be cleaned.", DEBUG_DEVELOPER); } } return $user; }
} } // Restore the #anchor to the original wantsurl. Note that this // will only work for internal auth plugins, SSO plugins such as // SAML / CAS / OIDC will have to handle this correctly directly. if ($anchor && isset($SESSION->wantsurl) && strpos($SESSION->wantsurl, '#') === false) { $wantsurl = new moodle_url($SESSION->wantsurl); $wantsurl->set_anchor(substr($anchor, 1)); $SESSION->wantsurl = $wantsurl->out(); } /// Check if the user has actually submitted login data to us if ($frm and isset($frm->username)) { // Login WITH cookies $frm->username = trim(core_text::strtolower($frm->username)); if (is_enabled_auth('none')) { if ($frm->username !== core_user::clean_field($frm->username, 'username')) { $errormsg = get_string('username') . ': ' . get_string("invalidusername"); $errorcode = 2; $user = null; } } if ($user) { //user already supplied by aut plugin prelogin hook } else { if ($frm->username == 'guest' and empty($CFG->guestloginbutton)) { $user = false; /// Can't log in as guest if guest button is disabled $frm = false; } else { if (empty($errormsg)) { $user = authenticate_user_login($frm->username, $frm->password, false, $errorcode);
/** * Update a user with a user object (will compare against the ID) * * @throws moodle_exception * @param stdClass $user the user to update * @param bool $updatepassword if true, authentication plugin will update password. * @param bool $triggerevent set false if user_updated event should not be triggred. * This will not affect user_password_updated event triggering. */ function user_update_user($user, $updatepassword = true, $triggerevent = true) { global $DB; // Set the timecreate field to the current time. if (!is_object($user)) { $user = (object) $user; } // Check username. if (isset($user->username)) { if ($user->username !== core_text::strtolower($user->username)) { throw new moodle_exception('usernamelowercase'); } else { if ($user->username !== core_user::clean_field($user->username, 'username')) { throw new moodle_exception('invalidusername'); } } } // Unset password here, for updating later, if password update is required. if ($updatepassword && isset($user->password)) { // Check password toward the password policy. if (!check_password_policy($user->password, $errmsg)) { throw new moodle_exception($errmsg); } $passwd = $user->password; unset($user->password); } // Make sure calendartype, if set, is valid. if (empty($user->calendartype)) { // Unset this variable, must be an empty string, which we do not want to update the calendartype to. unset($user->calendartype); } $user->timemodified = time(); // Validate user data object. $uservalidation = core_user::validate($user); if ($uservalidation !== true) { foreach ($uservalidation as $field => $message) { debugging("The property '{$field}' has invalid data and has been cleaned.", DEBUG_DEVELOPER); $user->{$field} = core_user::clean_field($user->{$field}, $field); } } $DB->update_record('user', $user); if ($updatepassword) { // Get full user record. $updateduser = $DB->get_record('user', array('id' => $user->id)); // If password was set, then update its hash. if (isset($passwd)) { $authplugin = get_auth_plugin($updateduser->auth); if ($authplugin->can_change_password()) { $authplugin->user_update_password($updateduser, $passwd); } } } // Trigger event if required. if ($triggerevent) { \core\event\user_updated::create_from_userid($user->id)->trigger(); } }
/** * Validates the standard sign-up data (except recaptcha that is validated by the form element). * * @param array $data the sign-up data * @param array $files files among the data * @return array list of errors, being the key the data element name and the value the error itself * @since Moodle 3.2 */ function signup_validate_data($data, $files) { global $CFG, $DB; $errors = array(); $authplugin = get_auth_plugin($CFG->registerauth); if ($DB->record_exists('user', array('username' => $data['username'], 'mnethostid' => $CFG->mnet_localhost_id))) { $errors['username'] = get_string('usernameexists'); } else { // Check allowed characters. if ($data['username'] !== core_text::strtolower($data['username'])) { $errors['username'] = get_string('usernamelowercase'); } else { if ($data['username'] !== core_user::clean_field($data['username'], 'username')) { $errors['username'] = get_string('invalidusername'); } } } // Check if user exists in external db. // TODO: maybe we should check all enabled plugins instead. if ($authplugin->user_exists($data['username'])) { $errors['username'] = get_string('usernameexists'); } if (!validate_email($data['email'])) { $errors['email'] = get_string('invalidemail'); } else { if ($DB->record_exists('user', array('email' => $data['email']))) { $errors['email'] = get_string('emailexists') . ' <a href="forgot_password.php">' . get_string('newpassword') . '?</a>'; } } if (empty($data['email2'])) { $errors['email2'] = get_string('missingemail'); } else { if ($data['email2'] != $data['email']) { $errors['email2'] = get_string('invalidemail'); } } if (!isset($errors['email'])) { if ($err = email_is_not_allowed($data['email'])) { $errors['email'] = $err; } } $errmsg = ''; if (!check_password_policy($data['password'], $errmsg)) { $errors['password'] = $errmsg; } // Validate customisable profile fields. (profile_validation expects an object as the parameter with userid set). $dataobject = (object) $data; $dataobject->id = 0; $errors += profile_validation($dataobject, $files); return $errors; }