/** * Authenticates authentication_info with the authenticating source, returning a simple boolean result. * * Note that, despite this function's name, there is no requirement that a password be part of the authentication_info. * Merely that enough information be provided in the authentication_info array to unequivocally authenticate the user. For * most authenticating authorities this will be the equivalent of a user name and password, but--again--there * is no restriction here. This is not, however, a "user exists in the system" function. It is expected that * the authenticating authority validate what ever is used as a password or the equivalent thereof. * * This function makes no attempt to match the given authentication_info with a Zikula user id (uid). It simply asks the * authenticating authority to authenticate the authentication_info provided. No "login" should take place as a result of * this authentication. * * This function may be called to initially authenticate a user during the registration process, or may be called * for a user already logged in to re-authenticate his password for a security-sensitive operation. This function * should merely authenticate the user, and not perform any additional login-related processes. * * This function differs from authenticateUser() in that no attempt is made to match the authentication_info with and map to a * Zikula user account. It does not return a Zikula user id (uid). * * Parameters passed in $args: * --------------------------- * array $args['authentication_info'] The information needed for this authenticationModule, including any user-entered * information. For the Users module, this contains the elements 'login_id' and 'pass'. * The 'login_id' element contains either the user name or the e-mail address of the * user logging in, depending on the authentication_method. The 'pass' contains the * password entered by the user. * array $args['authentication_method'] An array containing the authentication method, including the 'modname' (which should match this * module's module name), and the 'method' method name. For the Users module, 'modname' would * be 'Users' and 'method' would contain either 'email' or 'uname'. * * @param array $args All arguments passed to this function. * * @return boolean True if the authentication_info authenticates with the source; otherwise false on authentication failure. * * @throws Zikula_Exception_Fatal Thrown if invalid parameters are sent in $args. */ public function checkPassword(array $args) { // Note that this is a poor example function for external authenticationModules, because the authenticating // information for the Users module is stored in the users table, necessitating a lookup of the uid. // // For authentication modules other than the Users module, no attempt to look up the user account in the Users tables should be // made in the checkPassword function. if (!isset($args['authentication_info']) || !is_array($args['authentication_info']) || empty($args['authentication_info'])) { throw new Zikula_Exception_Fatal($this->__f('Invalid \'%1$s\' parameter received in a call to %2$s', array('authentication_info', __METHOD__))); } if (!isset($args['authentication_method']) || !is_array($args['authentication_method']) || empty($args['authentication_method'])) { throw new Zikula_Exception_Fatal($this->__f('Invalid \'%1$s\' parameter received in a call to %2$s', array('authentication_method', __METHOD__))); } $authenticationInfo = $args['authentication_info']; $authenticationMethod = $args['authentication_method']; $passwordAuthenticates = false; $getUidArgs = array( 'authentication_info' => $authenticationInfo, 'authentication_method' => $authenticationMethod, ); $uid = ModUtil::apiFunc($this->name, 'Authentication', 'getUidForAuthenticationInfo', $getUidArgs, 'Zikula_Api_AbstractAuthentication'); if ($uid) { if (!isset($authenticationInfo['pass']) || !is_string($authenticationInfo['pass']) || empty($authenticationInfo['pass'])) { // The user did not specify a password, or the one specified is invalid. throw new Zikula_Exception_Fatal($this->__('Error! A password must be provided.')); } // For a custom authenticationModule, we'd map the authenticationInfo to a uid above, and then execute the custom // authentication process here. On success the uid would be returned, otherwise false is returned. Note that // any "log in" into the Zikula site is not done here. This is simply verification that the authenticationInfo, // including the password, is valid as a unit. $userObj = UserUtil::getVars($uid, true); if (!$userObj) { // Must be a registration. Acting as an authenticationModule, we should not care at this point about the user's // account status. We will deal with the account status in a moment. $userObj = UserUtil::getVars($uid, true, '', true); if (!$userObj) { // Neither an account nor a pending registration request. This should really not happen since we have a uid. throw new Zikula_Exception_Fatal($this->__f('A user id was located, but the user account record could not be retrieved in a call to %1$s.', array(__METHOD__))); } } // Check for an empty password, or the special marker indicating that the account record does not // authenticate with a uname/password (or email/password, depending on the 'loginviaoption' setting) from // the Users module. An empty password can be created when an administrator creates a user registration // record pending e-mail verification and does not set a password for the user (the user will set it // upon verifying his email address). The special marker indicating that the account does not authenticate // with the Users module is used when a user registers a new account with the system using an authentication // method other than uname/pass or email/pass. In both cases, authentication automatically fails. if (!empty($userObj['pass']) && ($userObj['pass'] != Users_Constant::PWD_NO_USERS_AUTHENTICATION)) { // The following check for non-salted passwords and the old 'hash_method' field is to allow the admin to log in // during an upgrade from 1.2. // *** IMPORTANT *** // This needs to be kept for any version that allows an upgrade from Zikula 1.2.X. $methodSaltDelimPosition = strpos($userObj['pass'], Users_Constant::SALT_DELIM); $saltPassDelimPosition = ($methodSaltDelimPosition === false) ? false : strpos($userObj['pass'], Users_Constant::SALT_DELIM, ($methodSaltDelimPosition + 1)); if ($saltPassDelimPosition === false) { // Old style unsalted password with hash_method in separate field // If this release version of Zikula Users Module allows upgrade from 1.2.X, then this part must be // kept. If this release version of Zikula Users Module DOES NOT support upgrade from 1.2.X then this // is the part that can go away. if (!isset($userObj['hash_method'])) { // Something is horribly wrong. The password on the user account record does not look like the // new style of hashing, and yet the old-style hash method field is nowhere to be found. throw new Zikula_Exception_Fatal($this->__('Invalid account password state.')); } $currentPasswordHashed = $userObj['hash_method'] . '$$' . $userObj['pass']; } else { // New style salted password including hash method code. // If this release version of Zikula Users module does not allow upgrade from 1.2.X, then this // is the part to keep. $currentPasswordHashed = $userObj['pass']; } // *** IMPORTANT *** // End of old-style versus new-style hashing handling. When the possiblity to upgrade from 1.2.X is // removed from the released version of Zikula Users Module, then delete this section, and replace // $currentPasswordHashed with $userObj['pass'] in the call to passwordsMatch below. if (UserUtil::passwordsMatch($authenticationInfo['pass'], $currentPasswordHashed)) { // Password in $authenticationInfo['pass'] is good at this point. // *** IMPORTANT *** // Again, this section is for converting old-style hashing to new-style hashing. Same as noted // above applies to this section. // See if we need to convert the password hashing to the new configuration. if (version_compare($this->modinfo['version'], '2.0.0') >= 0) { // Check stored hash matches the current system type, if not convert it--but only if the module version is sufficient. // Note: this is purely specific to the Users module authentication. A custom module might do something similar if it // changed the way it stored some piece of data between versions, but in general this would be uncommon. list($currentPasswordHashCode, $currentPasswordSaltStr, $currentPasswordHashStr) = explode(Users_Constant::SALT_DELIM, $currentPasswordHashed); $systemHashMethodCode = UserUtil::getPasswordHashMethodCode($this->getVar('hash_method', 'sha256')); if (($systemHashMethodCode != $currentPasswordHashCode) || empty($currentPasswordSaltStr)) { if (!UserUtil::setPassword($authenticationInfo['pass'], $uid)) { LogUtil::log($this->__('Internal Error! Unable to update the user\'s password with the new hashing method and/or salt.'), 'CORE'); } } } // *** IMPORTANT *** // End of old-style to new-style hasing conversion. // The password is good, so the password is authenticated. $passwordAuthenticates = true; } } } if (!$passwordAuthenticates && !$this->request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) { if ($authenticationMethod['method'] == 'email') { $this->registerError($this->__('Sorry! The e-mail address or password you entered was incorrect.')); } else { $this->registerError($this->__('Sorry! The user name or password you entered was incorrect.')); } } return $passwordAuthenticates; }
/** * Check a lost password confirmation code. * * Parameters passed in the $args array: * ------------------------------------- * string $args['idfield'] Either 'uname' or 'email'. * string $args['id'] The user's user name or e-mail address, depending on the value of idfield. * string $args['code'] The confirmation code. * * @param array $args All parameters passed to this function. * * @return bool True if the new password was sent; otherwise false. */ public function checkConfirmationCode($args) { $codeIsGood = false; if (!isset($args['id']) || empty($args['id']) || !isset($args['idfield']) || empty($args['idfield']) || !isset($args['code']) || empty($args['code']) || (($args['idfield'] != 'uname') && ($args['idfield'] != 'email'))) { $this->registerError(LogUtil::getErrorMsgArgs()); return false; } $user = UserUtil::getVars($args['id'], true, $args['idfield']); if (!$user) { $this->registerError(LogUtil::getErrorMsgArgs()); return false; } else { // delete all the records for password reset confirmation that have expired $tables = DBUtil::getTables(); $verifychgColumn = $tables['users_verifychg_column']; $chgPassExpireDays = $this->getVar(Users_Constant::MODVAR_EXPIRE_DAYS_CHANGE_PASSWORD, Users_Constant::DEFAULT_EXPIRE_DAYS_CHANGE_PASSWORD); if ($chgPassExpireDays > 0) { $staleRecordUTC = new DateTime(null, new DateTimeZone('UTC')); $staleRecordUTC->modify("-{$chgPassExpireDays} days"); $staleRecordUTCStr = $staleRecordUTC->format(Users_Constant::DATETIME_FORMAT); $where = "({$verifychgColumn['created_dt']} < '{$staleRecordUTCStr}') AND ({$verifychgColumn['changetype']} = " . Users_Constant::VERIFYCHGTYPE_PWD . ")"; DBUtil::deleteWhere ('users_verifychg', $where); } $verifychgObj = DBUtil::selectObject('users_verifychg', "({$verifychgColumn['uid']} = {$user['uid']}) AND ({$verifychgColumn['changetype']} = " . Users_Constant::VERIFYCHGTYPE_PWD . ")"); if ($verifychgObj) { $codeIsGood = UserUtil::passwordsMatch($args['code'], $verifychgObj['verifycode']); } else { $this->registerError('Sorry! Could not retrieve a confirmation code for that account.'); } } return $codeIsGood; }
/** * Confirm the update of the email address. * * Available Get Parameters: * - confirmcode (string) The confirmation code. * * Parameters passed via the $args array: * -------------------------------------- * string $args['confirmcode'] Default value for the 'confirmcode' get parameter. Allows this function to be called internally. * * Parameters passed via GET: * -------------------------- * string confirmcode The confirmation code for verifying the change of e-mail address. * * Parameters passed via POST: * --------------------------- * None. * * Parameters passed via SESSION: * ------------------------------ * None. * * @param array $args All parameters passed to this function. * * @return bool True on success, otherwise false. */ public function confirmChEmail($args) { $confirmcode = $this->request->query->get('confirmcode', isset($args['confirmcode']) ? $args['confirmcode'] : null); if (!UserUtil::isLoggedIn()) { $this->registerError($this->__('Please log into your account in order to confirm your change of e-mail address.')) ->redirect(ModUtil::url($this->name, 'user', 'login', array('returnpage' => urlencode(ModUtil::url($this->name, 'user', 'confirmChEmail', array('confirmcode' => $confirmcode)))))); } // get user new email that is waiting for confirmation $preemail = ModUtil::apiFunc($this->name, 'user', 'getUserPreEmail'); $validCode = UserUtil::passwordsMatch($confirmcode, $preemail['verifycode']); if (!$preemail || !$validCode) { $this->registerError($this->__('Error! Your e-mail has not been found. After your request you have five days to confirm the new e-mail address.')) ->redirect(ModUtil::url($this->name, 'user', 'main')); } // user and confirmation code are correct. set the new email UserUtil::setVar('email', $preemail['newemail']); // the preemail record is deleted ModUtil::apiFunc($this->name, 'user', 'resetVerifyChgFor', array( 'uid' => $preemail['uid'], 'changetype'=> Users_Constant::VERIFYCHGTYPE_EMAIL, )); $this->registerStatus($this->__('Done! Changed your e-mail address.')) ->redirect(ModUtil::url($this->name, 'user', 'main')); }