From 03d820c788567c8559fca9fea2b5d2e45d43ee1d Mon Sep 17 00:00:00 2001 From: martinlanghoff Date: Thu, 4 Jan 2007 02:52:44 +0000 Subject: [PATCH] mnet: changes to core moode libs - get_user_listing() is now aware of remote users - require_login() is now aware of remote users - new auth plugins infrastructure in place - is_internal_auth() is now a deprecated legacy call - create_user_record()/update_user_record()/authenticate_user_login() handle multi-auth - is_mnet_remote_user() checks for remoteness - clean_param() handles PARAM_PEM and PARAM_BASE64 Also - user_login_string() knows to offer a link to remote users to login @ their host - XMLTDBTable field creation returns the field obj so that the caller has a chance to set additional properties such as comment --- lib/datalib.php | 6 +- lib/moodlelib.php | 320 ++++++++++++++++++------- lib/weblib.php | 3 + lib/xmldb/classes/XMLDBTable.class.php | 4 + 4 files changed, 241 insertions(+), 92 deletions(-) diff --git a/lib/datalib.php b/lib/datalib.php index 4c688448a8..d15891acaf 100644 --- a/lib/datalib.php +++ b/lib/datalib.php @@ -273,7 +273,7 @@ function get_users($get=true, $search='', $confirmed=false, $exceptions='', $sor */ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperpage=0, - $search='', $firstinitial='', $lastinitial='') { + $search='', $firstinitial='', $lastinitial='', $remotewhere='') { global $CFG; @@ -295,12 +295,14 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp $select .= ' AND lastname '. $LIKE .' \''. $lastinitial .'%\' '; } + $select .= $remotewhere; + if ($sort) { $sort = ' ORDER BY '. $sort .' '. $dir; } /// warning: will return UNCONFIRMED USERS - return get_records_sql("SELECT id, username, email, firstname, lastname, city, country, lastaccess, confirmed + return get_records_sql("SELECT id, username, email, firstname, lastname, city, country, lastaccess, confirmed, mnethostid FROM {$CFG->prefix}user WHERE $select $sort", $page, $recordsperpage); diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 60e6dcf506..f271cdfa1f 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -217,6 +217,17 @@ define('PARAM_SAFEDIR', 0x4000); */ define('PARAM_SEQUENCE', 0x8000); +/** + * PARAM_PEM - Privacy Enhanced Mail format + */ +define('PARAM_PEM', 0x10000); + +/** + * PARAM_BASE64 - Base 64 encoded format + */ +define('PARAM_BASE64', 0x20000); + + /// Page types /// /** * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php. @@ -245,6 +256,7 @@ define ('BLOG_SITE_LEVEL', 4); define ('BLOG_GLOBAL_LEVEL', 5); + /// PARAMETER HANDLING //////////////////////////////////////////////////// /** @@ -482,7 +494,54 @@ function clean_param($param, $type) { } } return $param; + case PARAM_PEM: + $param = trim($param); + // PEM formatted strings may contain letters/numbers and the symbols + // forward slash: / + // plus sign: + + // equal sign: = + // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes + if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) { + list($wholething, $body) = $matches; + unset($wholething, $matches); + $b64 = clean_param($body, PARAM_BASE64); + if (!empty($b64)) { + return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n"; + } else { + return ''; + } + } + return ''; + case PARAM_BASE64: + if (!empty($param)) { + // PEM formatted strings may contain letters/numbers and the symbols + // forward slash: / + // plus sign: + + // equal sign: = + error_log("Called with " . $param); + if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) { + return ''; + } + $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY); + // Each line of base64 encoded data must be 64 characters in + // length, except for the last line which may be less than (or + // equal to) 64 characters long. + for ($i=0, $j=count($lines); $i < $j; $i++) { + if ($i + 1 == $j) { + if (64 < strlen($lines[$i])) { + return ''; + } + continue; + } + if (64 != strlen($lines[$i])) { + return ''; + } + } + return implode("\n",$lines); + } else { + return ''; + } default: // throw error, switched parameters in optional_param or another serious problem error("Unknown parameter type: $type"); } @@ -1553,8 +1612,9 @@ function require_login($courseid=0, $autologinguest=true, $cm=null) { } /// check whether the user should be changing password + $userauth = get_auth_plugin($USER->auth); if (!empty($USER->preference['auth_forcepasswordchange'])){ - if (is_internal_auth() || $CFG->{'auth_'.$USER->auth.'_stdchangepassword'}){ + if ($userauth->can_change_password()) { $SESSION->wantsurl = $FULLME; if (empty($CFG->loginhttps)) { redirect($CFG->wwwroot .'/login/change_password.php'); @@ -1562,8 +1622,8 @@ function require_login($courseid=0, $autologinguest=true, $cm=null) { $wwwroot = str_replace('http:','https:', $CFG->wwwroot); redirect($wwwroot .'/login/change_password.php'); } - } elseif($CFG->changepassword) { - redirect($CFG->changepassword); + } elseif($userauth->change_password_url()) { + redirect($userauth->change_password_url(); } else { error('You cannot proceed without changing your password. However there is no available page for changing it. @@ -1731,6 +1791,10 @@ function require_logout() { if ($USER->auth == 'cas' && !empty($CFG->cas_enabled)) { require($CFG->dirroot.'/auth/cas/logout.php'); } + + require($CFG->dirroot.'/auth/mnet/auth.php'); + $authplugin = new auth_plugin_mnet(); + $authplugin->logout(); } if (ini_get_bool("register_globals") and check_php_version("4.3.0")) { @@ -2208,25 +2272,83 @@ function get_moodle_cookie() { } /** - * Returns true if an internal authentication method is being used. - * if method not specified then, global default is assumed + * Returns whether a given authentication plugin exists. * * @uses $CFG - * @param string $auth Form of authentication required - * @return bool - * @todo Outline auth types and provide code example + * @param string $auth Form of authentication to check for. Defaults to the + * global setting in {@link $CFG}. + * @return boolean Whether the plugin is available. */ -function is_internal_auth($auth='') { -/// Returns true if an internal authentication method is being used. -/// If auth not specified then global default is assumed +function exists_auth_plugin($auth='') { + global $CFG; + + // use the global default if not specified + if ($auth == '') { + $auth = $CFG->auth; + } + if (file_exists("{$CFG->dirroot}/auth/$auth/auth.php")) { + return is_readable("{$CFG->dirroot}/auth/$auth/auth.php"); + } + return false; +} +/** + * Checks if a given plugin is in the list of enabled authentication plugins. + * + * @param string $auth Authentication plugin. + * @return boolean Whether the plugin is enabled. + */ +function is_enabled_auth($auth='') { global $CFG; - if (empty($auth)) { + // use the global default if not specified + if ($auth == '') { + $auth = $CFG->auth; + } + return in_array($auth, explode(',', $CFG->auth_plugins_enabled)); +} + +/** + * Returns an authentication plugin instance. + * + * @uses $CFG + * @param string $auth Form of authentication required. Defaults to the + * global setting in {@link $CFG}. + * @return object An instance of the required authentication plugin. + */ +function get_auth_plugin($auth = '') { + global $CFG; + + // use the global default if not specified + if ($auth == '') { $auth = $CFG->auth; } - return ($auth == "email" || $auth == "none" || $auth == "manual"); + // TODO: plugin enabled? + + // check the plugin exists first + if (! exists_auth_plugin($auth)) { + error("Authentication plugin '$auth' not found."); + } + + // return auth plugin instance + require_once "{$CFG->dirroot}/auth/$auth/auth.php"; + $class = "auth_plugin_$auth"; + return new $class; +} + +/** + * Returns true if an internal authentication method is being used. + * if method not specified then, global default is assumed + * + * @uses $CFG + * @param string $auth Form of authentication required + * @return bool + * @todo Outline auth types and provide code example + */ +function is_internal_auth($auth='') { + $authplugin = get_auth_plugin($auth); // throws error if bad $auth + return $authplugin->is_internal(); } /** @@ -2262,8 +2384,10 @@ function create_user_record($username, $password, $auth='') { //just in case check text case $username = trim(moodle_strtolower($username)); - if (function_exists('auth_get_userinfo')) { - if ($newinfo = auth_get_userinfo($username)) { + $authplugin = get_auth_plugin($auth); + + if (method_exists($authplugin, 'get_userinfo')) { + if ($newinfo = $authplugin->get_userinfo($username)) { $newinfo = truncate_userinfo($newinfo); foreach ($newinfo as $key => $value){ $newuser->$key = addslashes(stripslashes($value)); // Just in case @@ -2284,6 +2408,7 @@ function create_user_record($username, $password, $auth='') { $newuser->confirmed = 1; $newuser->lastip = getremoteaddr(); $newuser->timemodified = time(); + $newuser->mnethostid = $CFG->mnet_localhost_id; if (insert_record('user', $newuser)) { $user = get_complete_user_data('username', $newuser->username); @@ -2302,23 +2427,21 @@ function create_user_record($username, $password, $auth='') { * @param string $username New user's username to add to record * @return user A {@link $USER} object */ -function update_user_record($username) { - global $CFG; - - if (function_exists('auth_get_userinfo')) { +function update_user_record($username, $authplugin) { + if (method_exists($authplugin, 'get_userinfo')) { $username = trim(moodle_strtolower($username)); /// just in case check text case $oldinfo = get_record('user', 'username', $username, '','','','', 'username, auth'); - $authconfig = get_config('auth/' . $oldinfo->auth); + $userauth = get_auth_plugin($oldinfo->auth); - if ($newinfo = auth_get_userinfo($username)) { + if ($newinfo = $authplugin->get_userinfo($username)) { $newinfo = truncate_userinfo($newinfo); foreach ($newinfo as $key => $value){ $confkey = 'field_updatelocal_' . $key; - if (!empty($authconfig->$confkey) && $authconfig->$confkey === 'onlogin') { + if (!empty($userauth->config->$confkey) and $userauth->config->$confkey === 'onlogin') { $value = addslashes(stripslashes($value)); // Just in case set_field('user', $key, $value, 'username', $username) - || error_log("Error updating $key for $username"); + or error_log("Error updating $key for $username"); } } } @@ -2395,63 +2518,67 @@ function authenticate_user_login($username, $password) { global $CFG; - // First try to find the user in the database + // default to manual if global auth is undefined or broken + if (empty($CFG->auth_plugins_enabled)) { + $CFG->auth_plugins_enabled = empty($CFG->auth) ? 'manual' : $CFG->auth; + } + // if blank, set default auth to first enabled auth plugin + if (empty($CFG->auth)) { + $auths = explode(',', $CFG->auth_plugins_enabled); + $CFG->auth = $auths[0]; + } + // if user not found, use site auth if (!$user = get_complete_user_data('username', $username)) { + $user = new object(); $user->id = 0; // Not a user - $user->auth = $CFG->auth; + $auth = $CFG->auth_plugins_enabled; } // Sort out the authentication method we are using. - - if (empty($CFG->auth)) { - $CFG->auth = 'manual'; // Default authentication module - } - if (empty($user->auth)) { // For some reason it isn't set yet $primadmin = get_admin(); if (!empty($user->id) && (($user->id==$primadmin->id) || isguest($user->id))) { - $auth = 'manual'; // Always assume these guys are internal - } else { - $auth = $CFG->auth; // Normal users default to site method + $auth = 'manual'; // always assume these guys are internal } - // update user record from external DB - if ($user->auth != 'manual' && $user->auth != 'email') { - $user = update_user_record($username); + else { + $auth = $CFG->auth_plugins_enabled; // default to site method } } else { $auth = $user->auth; } - if (detect_munged_arguments($auth, 0)) { // For safety on the next require - return false; - } - - if (!file_exists($CFG->dirroot .'/auth/'. $auth .'/lib.php')) { - $auth = 'manual'; // Can't find auth module, default to internal - } + // walk each authentication plugin, in order + $auths = explode(',', $auth); + foreach ($auths as $auth) { + $authplugin = get_auth_plugin($auth); - require_once($CFG->dirroot .'/auth/'. $auth .'/lib.php'); + // on auth fail, log and fall through to the next plugin + if (!$authplugin->user_login($username, $password)) { + add_to_log(0, 'login', 'error', 'index.php', $username); + error_log("[client {$_SERVER['REMOTE_ADDR']}] $CFG->wwwroot Auth=$auth Failed Login: $username {$_SERVER['HTTP_USER_AGENT']}"); + continue; + } - if (auth_user_login($username, $password)) { // Successful authentication + // successful authentication if ($user->id) { // User already exists in database if (empty($user->auth)) { // For some reason auth isn't set yet set_field('user', 'auth', $auth, 'username', $username); } update_internal_user_password($user, $password); - if (!is_internal_auth()) { // update user record from external DB - $user = update_user_record($username); + if (!$authplugin->is_internal()) { // update user record from external DB + $user = update_user_record($username, get_auth_plugin($user->auth)); } } else { $user = create_user_record($username, $password, $auth); } - // fix for MDL-6928 - if (function_exists('auth_iscreator')) { + // fix for MDL-6928 + if (method_exists($authplugin, 'iscreator')) { $sitecontext = get_context_instance(CONTEXT_SYSTEM); if ($creatorroles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { $creatorrole = array_shift($creatorroles); // We can only use one, let's use the first one // Check if the user is a creator - if (auth_iscreator($username)) { // Following calls will not create duplicates + if ($authplugin->iscreator($username)) { // Following calls will not create duplicates role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, $auth); } else { role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id); @@ -2471,11 +2598,12 @@ function authenticate_user_login($username, $password) { return $user; - } else { - add_to_log(0, 'login', 'error', 'index.php', $username); - error_log('[client '.$_SERVER['REMOTE_ADDR']."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']); - return false; - } + } + + // failed if all the plugins have failed + add_to_log(0, 'login', 'error', 'index.php', $username); + error_log('[client '.$_SERVER['REMOTE_ADDR']."] $CFG->wwwroot Failed Login: $username ".$_SERVER['HTTP_USER_AGENT']); + return false; } /** @@ -2549,21 +2677,14 @@ function hash_internal_user_password($password) { function update_internal_user_password(&$user, $password, $storeindb=true) { global $CFG; - if (!empty($CFG->{$user->auth.'_preventpassindb'})) { + $authplugin = get_auth_plugin($user->auth); + if (!empty($authplugin->config->preventpassindb) /*|| $storeindb === false */) { $hashedpassword = 'not cached'; } else { $hashedpassword = hash_internal_user_password($password); } - if ($user->password != $hashedpassword) { - if ($storeindb) { - if (!set_field('user', 'password', $hashedpassword, 'username', $user->username)) { - return false; - } - } - $user->password = $hashedpassword; - } - return true; + return set_field('user', 'password', $hashedpassword, 'username', $user->username); } /** @@ -3277,27 +3398,17 @@ function reset_password_and_mail($user) { $from = get_admin(); $external = false; - if (!is_internal_auth($user->auth)) { - include_once($CFG->dirroot . '/auth/' . $user->auth . '/lib.php'); - if (empty($CFG->{'auth_'.$user->auth.'_stdchangepassword'}) - || !function_exists('auth_user_update_password')) { - trigger_error("Attempt to reset user password for user $user->username with Auth $user->auth."); - return false; - } else { - $external = true; - } + + $userauth = get_auth_plugin($user->auth); + if (!$userauth->can_change_password()) { + trigger_error("Attempt to reset user password for user $user->username with Auth $user->auth."); + return false; } $newpassword = generate_password(); - if ($external) { - if (!auth_user_update_password($user->username, $newpassword)) { - error("Could not set user password!"); - } - } else { - if (! set_field("user", "password", md5($newpassword), "id", $user->id) ) { - error("Could not set user password!"); - } + if (!$userauth->user_update_password($user->username, $newpassword)) { + error("Could not set user password!"); } $a->firstname = $user->firstname; @@ -5831,14 +5942,8 @@ function zip_files ($originalfiles, $destination) { $filestozip .= " "; } //Construct the command - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $separator = ' &'; - $command= substr($CFG->dataroot, 0, 2).$separator; // change to drive with moodledata - } else { - $separator = ' ;'; - $command= ''; - } - $command .= 'cd '.escapeshellarg($origpath).$separator. + $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;'; + $command = 'cd '.escapeshellarg($origpath).$separator. escapeshellarg($CFG->zip).' -r '. escapeshellarg(cleardoubleslashes("$destpath/$destfilename")).' '.$filestozip; //All converted to backslashes in WIN @@ -6462,6 +6567,41 @@ function loadeditor($args) { return editorObject::loadeditor($args); } +/** + * Returns whether or not the user object is a remote MNET user. This function + * is in moodlelib because it does not rely on loading any of the MNET code. + * + * @param object $user A valid user object + * @return bool True if the user is from a remote Moodle. + */ +function is_mnet_remote_user($user) { + global $CFG; + + if (!isset($CFG->mnet_localhost_id)) { + include_once $CFG->dirroot . '/mnet/lib.php'; + $env = new mnet_environment(); + $env->init(); + unset($env); + } + + return ($user->mnethostid != $CFG->mnet_localhost_id); +} + +/** + * Checks if a given plugin is in the list of enabled enrolment plugins. + * + * @param string $auth Enrolment plugin. + * @return boolean Whether the plugin is enabled. + */ +function is_enabled_enrol($enrol='') { + global $CFG; + + // use the global default if not specified + if ($enrol == '') { + $enrol = $CFG->enrol; + } + return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled)); +} // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: ?> diff --git a/lib/weblib.php b/lib/weblib.php index 16281ace9d..099b14a602 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -2695,6 +2695,9 @@ function user_login_string($course=NULL, $user=NULL) { $fullname = fullname($user, true); $username = "wwwroot/user/view.php?id=$user->id&course=$course->id\">$fullname"; + if (is_mnet_remote_user($user) and $idprovider = get_record('mnet_host', 'id', $user->mnethostid)) { + $username .= " from wwwroot}\">{$idprovider->name}"; + } if (isset($user->username) && $user->username == 'guest') { $loggedinas = $realuserinfo.get_string('loggedinasguest'). " (".get_string('login').')'; diff --git a/lib/xmldb/classes/XMLDBTable.class.php b/lib/xmldb/classes/XMLDBTable.class.php index 0b5497aed9..621d1a8e16 100644 --- a/lib/xmldb/classes/XMLDBTable.class.php +++ b/lib/xmldb/classes/XMLDBTable.class.php @@ -85,6 +85,8 @@ class XMLDBTable extends XMLDBObject { $this->calculateHash(true); /// We have one new field, so the table has changed $this->setChanged(true); + + return $field; } /** @@ -705,6 +707,8 @@ class XMLDBTable extends XMLDBObject { $field = new XMLDBField($name); $field->setAttributes($type, $precision, $unsigned, $notnull, $sequence, $enum, $enumvalues, $default); $this->addField($field, $previous); + + return $field; } /** -- 2.39.5