/** * Connect the user with an external source. * * This controller method is meant to be used with plugins that set its data array to work. * Events: ConnectData * * @since 2.0.0 * @access public * * @param string $Method Used to register multiple providers on ConnectData event. */ public function connect($Method) { $this->addJsFile('entry.js'); $this->View = 'connect'; $IsPostBack = $this->Form->isPostBack() && $this->Form->getFormValue('Connect', null) !== null; $UserSelect = $this->Form->getFormValue('UserSelect'); if (!$IsPostBack) { // Here are the initial data array values. that can be set by a plugin. $Data = array('Provider' => '', 'ProviderName' => '', 'UniqueID' => '', 'FullName' => '', 'Name' => '', 'Email' => '', 'Photo' => '', 'Target' => $this->target()); $this->Form->setData($Data); $this->Form->addHidden('Target', $this->Request->get('Target', '/')); } // The different providers can check to see if they are being used and modify the data array accordingly. $this->EventArguments = array($Method); // Fire ConnectData event & error handling. $currentData = $this->Form->formValues(); // Filter the form data for users here. SSO plugins must reset validated data each postback. $filteredData = Gdn::userModel()->filterForm($currentData, true); $filteredData = array_replace($filteredData, arrayTranslate($currentData, ['TransientKey', 'hpt'])); unset($filteredData['Roles'], $filteredData['RoleID']); $this->Form->formValues($filteredData); try { $this->EventArguments['Form'] = $this->Form; $this->fireEvent('ConnectData'); $this->fireEvent('AfterConnectData'); } catch (Gdn_UserException $Ex) { $this->Form->addError($Ex); return $this->render('ConnectError'); } catch (Exception $Ex) { if (Debug()) { $this->Form->addError($Ex); } else { $this->Form->addError('There was an error fetching the connection data.'); } return $this->render('ConnectError'); } if (!UserModel::noEmail()) { if (!$this->Form->getFormValue('Email') || $this->Form->getFormValue('EmailVisible')) { $this->Form->setFormValue('EmailVisible', true); $this->Form->addHidden('EmailVisible', true); if ($IsPostBack) { $this->Form->setFormValue('Email', val('Email', $currentData)); } } } $FormData = $this->Form->formValues(); // debug // Make sure the minimum required data has been provided to the connect. if (!$this->Form->getFormValue('Provider')) { $this->Form->addError('ValidateRequired', t('Provider')); } if (!$this->Form->getFormValue('UniqueID')) { $this->Form->addError('ValidateRequired', t('UniqueID')); } if (!$this->data('Verified')) { // Whatever event handler catches this must Set the data 'Verified' to true to prevent a random site from connecting without credentials. // This must be done EVERY postback and is VERY important. $this->Form->addError('The connection data has not been verified.'); } if ($this->Form->errorCount() > 0) { return $this->render(); } $UserModel = Gdn::userModel(); // Check to see if there is an existing user associated with the information above. $Auth = $UserModel->getAuthentication($this->Form->getFormValue('UniqueID'), $this->Form->getFormValue('Provider')); $UserID = val('UserID', $Auth); // Check to synchronise roles upon connecting. if (($this->data('Trusted') || c('Garden.SSO.SyncRoles')) && $this->Form->getFormValue('Roles', null) !== null) { $SaveRoles = $SaveRolesRegister = true; // Translate the role names to IDs. $Roles = $this->Form->getFormValue('Roles', null); $Roles = RoleModel::getByName($Roles); $RoleIDs = array_keys($Roles); if (empty($RoleIDs)) { // The user must have at least one role. This protects that. $RoleIDs = $this->UserModel->newUserRoleIDs(); } if (c('Garden.SSO.SyncRolesBehavior') === 'register') { $SaveRoles = false; } $this->Form->setFormValue('RoleID', $RoleIDs); } else { $SaveRoles = false; $SaveRolesRegister = false; } if ($UserID) { // The user is already connected. $this->Form->setFormValue('UserID', $UserID); if (c('Garden.Registration.ConnectSynchronize', true)) { $User = Gdn::userModel()->getID($UserID, DATASET_TYPE_ARRAY); $Data = $this->Form->formValues(); // Don't overwrite the user photo if the user uploaded a new one. $Photo = val('Photo', $User); if (!val('Photo', $Data) || $Photo && !isUrl($Photo)) { unset($Data['Photo']); } // Synchronize the user's data. $UserModel->save($Data, array('NoConfirmEmail' => true, 'FixUnique' => true, 'SaveRoles' => $SaveRoles)); } // Always save the attributes because they may contain authorization information. if ($Attributes = $this->Form->getFormValue('Attributes')) { $UserModel->saveAttribute($UserID, $Attributes); } // Sign the user in. Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // $this->_setRedirect(TRUE); $this->_setRedirect($this->Request->get('display') == 'popup'); } elseif ($this->Form->getFormValue('Name') || $this->Form->getFormValue('Email')) { $NameUnique = c('Garden.Registration.NameUnique', true); $EmailUnique = c('Garden.Registration.EmailUnique', true); $AutoConnect = c('Garden.Registration.AutoConnect'); if ($IsPostBack && $this->Form->getFormValue('ConnectName')) { $searchName = $this->Form->getFormValue('ConnectName'); } else { $searchName = $this->Form->getFormValue('Name'); } // Get the existing users that match the name or email of the connection. $Search = false; if ($searchName && $NameUnique) { $UserModel->SQL->orWhere('Name', $searchName); $Search = true; } if ($this->Form->getFormValue('Email') && ($EmailUnique || $AutoConnect)) { $UserModel->SQL->orWhere('Email', $this->Form->getFormValue('Email')); $Search = true; } if (is_numeric($UserSelect)) { $UserModel->SQL->orWhere('UserID', $UserSelect); $Search = true; } if ($Search) { $ExistingUsers = $UserModel->getWhere()->resultArray(); } else { $ExistingUsers = array(); } // Check to automatically link the user. if ($AutoConnect && count($ExistingUsers) > 0) { if ($IsPostBack && $this->Form->getFormValue('ConnectName')) { $this->Form->setFormValue('Name', $this->Form->getFormValue('ConnectName')); } foreach ($ExistingUsers as $Row) { if (strcasecmp($this->Form->getFormValue('Email'), $Row['Email']) === 0) { $UserID = $Row['UserID']; $this->Form->setFormValue('UserID', $UserID); $Data = $this->Form->formValues(); if (c('Garden.Registration.ConnectSynchronize', true)) { // Don't overwrite a photo if the user has already uploaded one. $Photo = val('Photo', $Row); if (!val('Photo', $Data) || $Photo && !stringBeginsWith($Photo, 'http')) { unset($Data['Photo']); } $UserModel->save($Data, array('NoConfirmEmail' => true, 'FixUnique' => true, 'SaveRoles' => $SaveRoles)); } if ($Attributes = $this->Form->getFormValue('Attributes')) { $UserModel->saveAttribute($UserID, $Attributes); } // Save the userauthentication link. $UserModel->saveAuthentication(array('UserID' => $UserID, 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID'))); // Sign the user in. Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // $this->_setRedirect(TRUE); $this->_setRedirect($this->Request->get('display') == 'popup'); $this->render(); return; } } } $CurrentUserID = Gdn::session()->UserID; // Massage the existing users. foreach ($ExistingUsers as $Index => $UserRow) { if ($EmailUnique && $UserRow['Email'] == $this->Form->getFormValue('Email')) { $EmailFound = $UserRow; break; } if ($UserRow['Name'] == $this->Form->getFormValue('Name')) { $NameFound = $UserRow; } if ($CurrentUserID > 0 && $UserRow['UserID'] == $CurrentUserID) { unset($ExistingUsers[$Index]); $CurrentUserFound = true; } } if (isset($EmailFound)) { // The email address was found and can be the only user option. $ExistingUsers = array($UserRow); $this->setData('NoConnectName', true); } elseif (isset($CurrentUserFound)) { $ExistingUsers = array_merge(array('UserID' => 'current', 'Name' => sprintf(t('%s (Current)'), Gdn::session()->User->Name)), $ExistingUsers); } if (!isset($NameFound) && !$IsPostBack) { $this->Form->setFormValue('ConnectName', $this->Form->getFormValue('Name')); } $this->setData('ExistingUsers', $ExistingUsers); if (UserModel::noEmail()) { $EmailValid = true; } else { $EmailValid = validateRequired($this->Form->getFormValue('Email')); } if ((!$UserSelect || $UserSelect == 'other') && $this->Form->getFormValue('Name') && $EmailValid && (!is_array($ExistingUsers) || count($ExistingUsers) == 0)) { // There is no existing user with the suggested name so we can just create the user. $User = $this->Form->formValues(); $User['Password'] = randomString(50); // some password is required $User['HashMethod'] = 'Random'; $User['Source'] = $this->Form->getFormValue('Provider'); $User['SourceID'] = $this->Form->getFormValue('UniqueID'); $User['Attributes'] = $this->Form->getFormValue('Attributes', null); $User['Email'] = $this->Form->getFormValue('ConnectEmail', $this->Form->getFormValue('Email', null)); $UserID = $UserModel->register($User, array('CheckCaptcha' => false, 'ValidateEmail' => false, 'NoConfirmEmail' => true, 'SaveRoles' => $SaveRolesRegister)); $User['UserID'] = $UserID; $this->Form->setValidationResults($UserModel->validationResults()); if ($UserID) { $UserModel->saveAuthentication(array('UserID' => $UserID, 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID'))); $this->Form->setFormValue('UserID', $UserID); $this->Form->setFormValue('UserSelect', false); Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // Send the welcome email. if (c('Garden.Registration.SendConnectEmail', false)) { try { $UserModel->sendWelcomeEmail($UserID, '', 'Connect', array('ProviderName' => $this->Form->getFormValue('ProviderName', $this->Form->getFormValue('Provider', 'Unknown')))); } catch (Exception $Ex) { // Do nothing if emailing doesn't work. } } $this->_setRedirect(true); } } } // Save the user's choice. if ($IsPostBack) { // The user has made their decision. $PasswordHash = new Gdn_PasswordHash(); if (!$UserSelect || $UserSelect == 'other') { // The user entered a username. $ConnectNameEntered = true; if ($this->Form->validateRule('ConnectName', 'ValidateRequired')) { $ConnectName = $this->Form->getFormValue('ConnectName'); $User = false; if (c('Garden.Registration.NameUnique')) { // Check to see if there is already a user with the given name. $User = $UserModel->getWhere(array('Name' => $ConnectName))->firstRow(DATASET_TYPE_ARRAY); } if (!$User) { $this->Form->validateRule('ConnectName', 'ValidateUsername'); } } } else { // The user selected an existing user. $ConnectNameEntered = false; if ($UserSelect == 'current') { if (Gdn::session()->UserID == 0) { // This shouldn't happen, but a use could sign out in another browser and click submit on this form. $this->Form->addError('@You were unexpectedly signed out.'); } else { $UserSelect = Gdn::session()->UserID; } } $User = $UserModel->getID($UserSelect, DATASET_TYPE_ARRAY); } if (isset($User) && $User) { // Make sure the user authenticates. if (!$User['UserID'] == Gdn::session()->UserID) { if ($this->Form->validateRule('ConnectPassword', 'ValidateRequired', sprintf(t('ValidateRequired'), t('Password')))) { try { if (!$PasswordHash->checkPassword($this->Form->getFormValue('ConnectPassword'), $User['Password'], $User['HashMethod'], $this->Form->getFormValue('ConnectName'))) { if ($ConnectNameEntered) { $this->Form->addError('The username you entered has already been taken.'); } else { $this->Form->addError('The password you entered is incorrect.'); } } } catch (Gdn_UserException $Ex) { $this->Form->addError($Ex); } } } } elseif ($this->Form->errorCount() == 0) { // The user doesn't exist so we need to add another user. $User = $this->Form->formValues(); $User['Name'] = $User['ConnectName']; $User['Password'] = randomString(50); // some password is required $User['HashMethod'] = 'Random'; $UserID = $UserModel->register($User, array('CheckCaptcha' => false, 'NoConfirmEmail' => true, 'SaveRoles' => $SaveRolesRegister)); $User['UserID'] = $UserID; $this->Form->setValidationResults($UserModel->validationResults()); if ($UserID && c('Garden.Registration.SendConnectEmail', false)) { // Send the welcome email. $UserModel->sendWelcomeEmail($UserID, '', 'Connect', array('ProviderName' => $this->Form->getFormValue('ProviderName', $this->Form->getFormValue('Provider', 'Unknown')))); } } if ($this->Form->errorCount() == 0) { // Save the authentication. if (isset($User) && val('UserID', $User)) { $UserModel->saveAuthentication(array('UserID' => $User['UserID'], 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID'))); $this->Form->setFormValue('UserID', $User['UserID']); } // Sign the appropriate user in. Gdn::session()->start($this->Form->getFormValue('UserID'), true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); $this->_setRedirect(true); } } $this->render(); }
/** * SSO facilitator page. Plugins use event `ConnectData` to complete SSO connections. * * Users only see this page for non-seamless connections that prompt them to finish connecting * by entering a username and/or password (and possibly email). * * @since 2.0.0 * @access public * * @param string $Method Used to register multiple providers on ConnectData event. */ public function connect($Method) { // Basic page setup. $this->addJsFile('entry.js'); $this->View = 'connect'; $this->addDefinition('Username already exists.', t('Username already exists.')); $this->addDefinition('Choose a name to identify yourself on the site.', t('Choose a name to identify yourself on the site.')); // Determine what step in the process we're at. $IsPostBack = $this->Form->isPostBack() && $this->Form->getFormValue('Connect', null) !== null; $UserSelect = $this->Form->getFormValue('UserSelect'); /** * When a user is connecting through SSO she is prompted to choose a username. * If she chooses an existing username, she is prompted to enter the password to claim it. * Setting AllowConnect = false disables that workflow, forcing the user to choose a unique username. */ $allowConnect = c('Garden.Registration.AllowConnect', true); $this->setData('AllowConnect', $allowConnect); $this->addDefinition('AllowConnect', $allowConnect); if (!$IsPostBack) { // Initialize data array that can be set by a plugin. $Data = ['Provider' => '', 'ProviderName' => '', 'UniqueID' => '', 'FullName' => '', 'Name' => '', 'Email' => '', 'Photo' => '', 'Target' => $this->target()]; $this->Form->setData($Data); $this->Form->addHidden('Target', $this->Request->get('Target', '/')); } // SSO providers can check to see if they are being used and modify the data array accordingly. $this->EventArguments = [$Method]; // Filter the form data for users. // SSO plugins must reset validated data each postback. $currentData = $this->Form->formValues(); $filteredData = Gdn::userModel()->filterForm($currentData, true); $filteredData = array_replace($filteredData, arrayTranslate($currentData, ['TransientKey', 'hpt'])); unset($filteredData['Roles'], $filteredData['RoleID']); $this->Form->formValues($filteredData); // Fire ConnectData event & error handling. try { // Where your SSO plugin does magic. $this->EventArguments['Form'] = $this->Form; $this->fireEvent('ConnectData'); $this->fireEvent('AfterConnectData'); } catch (Gdn_UserException $Ex) { // Your SSO magic said no. $this->Form->addError($Ex); return $this->render('ConnectError'); } catch (Exception $Ex) { // Your SSO magic blew up. if (debug()) { $this->Form->addError($Ex); } else { $this->Form->addError('There was an error fetching the connection data.'); } return $this->render('ConnectError'); } // Allow a provider to not send an email address but require one be manually entered. if (!UserModel::noEmail()) { $emailProvided = $this->Form->getFormValue('Email'); $emailRequested = $this->Form->getFormValue('EmailVisible'); if (!$emailProvided || $emailRequested) { $this->Form->setFormValue('EmailVisible', true); $this->Form->addHidden('EmailVisible', true); if ($IsPostBack) { $this->Form->setFormValue('Email', val('Email', $currentData)); } } if ($IsPostBack && $emailRequested) { $this->Form->validateRule('Email', 'ValidateRequired'); $this->Form->validateRule('Email', 'ValidateEmail'); } } // Make sure the minimum required data has been provided by the connection. if (!$this->Form->getFormValue('Provider')) { $this->Form->addError('ValidateRequired', t('Provider')); } if (!$this->Form->getFormValue('UniqueID')) { $this->Form->addError('ValidateRequired', t('UniqueID')); } if (!$this->data('Verified')) { // Whatever event handler catches this must set the data 'Verified' = true // to prevent a random site from connecting without credentials. // This must be done EVERY postback and is VERY important. $this->Form->addError(t('The connection data has not been verified.')); } // If we've accrued errors, stop here and show them. if ($this->Form->errorCount() > 0) { $this->render(); return; } // Check if we need to sync roles if (($this->data('Trusted') || c('Garden.SSO.SyncRoles')) && $this->Form->getFormValue('Roles', null) !== null) { $SaveRoles = $SaveRolesRegister = true; // Translate the role names to IDs. $Roles = $this->Form->getFormValue('Roles', null); $Roles = RoleModel::getByName($Roles); $RoleIDs = array_keys($Roles); // Ensure user has at least one role. if (empty($RoleIDs)) { $RoleIDs = $this->UserModel->newUserRoleIDs(); } // Allow role syncing to only happen on first connect. if (c('Garden.SSO.SyncRolesBehavior') === 'register') { $SaveRoles = false; } $this->Form->setFormValue('RoleID', $RoleIDs); } else { $SaveRoles = false; $SaveRolesRegister = false; } $UserModel = Gdn::userModel(); // Find an existing user associated with this provider & uniqueid. $Auth = $UserModel->getAuthentication($this->Form->getFormValue('UniqueID'), $this->Form->getFormValue('Provider')); $UserID = val('UserID', $Auth); // The user is already in the UserAuthentication table if ($UserID) { $this->Form->setFormValue('UserID', $UserID); // Update their info. if (c('Garden.Registration.ConnectSynchronize', true)) { $User = Gdn::userModel()->getID($UserID, DATASET_TYPE_ARRAY); $Data = $this->Form->formValues(); // Don't overwrite the user photo if the user uploaded a new one. $Photo = val('Photo', $User); if (!val('Photo', $Data) || $Photo && !isUrl($Photo)) { unset($Data['Photo']); } // Synchronize the user's data. $UserModel->save($Data, ['NoConfirmEmail' => true, 'FixUnique' => true, 'SaveRoles' => $SaveRoles]); $this->EventArguments['UserID'] = $UserID; $this->fireEvent('AfterConnectSave'); } // Always save the attributes because they may contain authorization information. if ($Attributes = $this->Form->getFormValue('Attributes')) { $UserModel->saveAttribute($UserID, $Attributes); } // Sign the user in. Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // Send them on their way. $this->_setRedirect(Gdn::request()->get('display') === 'popup'); // If a name of email has been provided } elseif ($this->Form->getFormValue('Name') || $this->Form->getFormValue('Email')) { // Decide how to handle our first time connecting. $NameUnique = c('Garden.Registration.NameUnique', true); $EmailUnique = c('Garden.Registration.EmailUnique', true); $AutoConnect = c('Garden.Registration.AutoConnect'); // Decide which name to search for. if ($IsPostBack && $this->Form->getFormValue('ConnectName')) { $searchName = $this->Form->getFormValue('ConnectName'); } else { $searchName = $this->Form->getFormValue('Name'); } // Find existing users that match the name or email of the connection. // First, discover if we have search criteria. $Search = false; $ExistingUsers = []; if ($searchName && $NameUnique) { $UserModel->SQL->orWhere('Name', $searchName); $Search = true; } if ($this->Form->getFormValue('Email') && ($EmailUnique || $AutoConnect)) { $UserModel->SQL->orWhere('Email', $this->Form->getFormValue('Email')); $Search = true; } if (is_numeric($UserSelect)) { $UserModel->SQL->orWhere('UserID', $UserSelect); $Search = true; } // Now do the search if we found some criteria. if ($Search) { $ExistingUsers = $UserModel->getWhere()->resultArray(); } // Get the email and decide if we can safely find a match. $submittedEmail = $this->Form->getFormValue('Email'); $canMatchEmail = strlen($submittedEmail) > 0 && !UserModel::noEmail(); // Check to automatically link the user. if ($AutoConnect && count($ExistingUsers) > 0) { if ($IsPostBack && $this->Form->getFormValue('ConnectName')) { $this->Form->setFormValue('Name', $this->Form->getFormValue('ConnectName')); } if ($canMatchEmail) { // Check each existing user for an exact email match. foreach ($ExistingUsers as $Row) { if (strcasecmp($submittedEmail, $Row['Email']) === 0) { // Add the UserID to the form, then get the unified user data set from it. $UserID = $Row['UserID']; $this->Form->setFormValue('UserID', $UserID); $Data = $this->Form->formValues(); // User synchronization. if (c('Garden.Registration.ConnectSynchronize', true)) { // Don't overwrite a photo if the user has already uploaded one. $Photo = val('Photo', $Row); if (!val('Photo', $Data) || $Photo && !stringBeginsWith($Photo, 'http')) { unset($Data['Photo']); } // Update the user. $UserModel->save($Data, ['NoConfirmEmail' => true, 'FixUnique' => true, 'SaveRoles' => $SaveRoles]); $this->EventArguments['UserID'] = $UserID; $this->fireEvent('AfterConnectSave'); } // Always save the attributes because they may contain authorization information. if ($Attributes = $this->Form->getFormValue('Attributes')) { $UserModel->saveAttribute($UserID, $Attributes); } // Save the user authentication association. $UserModel->saveAuthentication(['UserID' => $UserID, 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID')]); // Sign the user in. Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); $this->_setRedirect(Gdn::request()->get('display') === 'popup'); $this->render(); return; } } } } // Did not autoconnect! // Explore alternatives for a first-time connection. // This will be zero for a guest. $CurrentUserID = Gdn::session()->UserID; // Evaluate the existing users for matches. foreach ($ExistingUsers as $Index => $UserRow) { if ($EmailUnique && $canMatchEmail && $UserRow['Email'] == $submittedEmail) { // An email match overrules any other options. $EmailFound = $UserRow; break; } // Detect a simple name match. if ($UserRow['Name'] == $this->Form->getFormValue('Name')) { $NameFound = $UserRow; } // Detect if we have a match on the current user session. if ($CurrentUserID > 0 && $UserRow['UserID'] == $CurrentUserID) { unset($ExistingUsers[$Index]); $CurrentUserFound = true; } } // Handle special cases for what we matched on. if (isset($EmailFound)) { // The email address was found and can be the only user option. $ExistingUsers = [$UserRow]; $this->setData('NoConnectName', true); } elseif (isset($CurrentUserFound)) { // If we're already logged in to Vanilla, assume that's an option we want. $ExistingUsers = array_merge(['UserID' => 'current', 'Name' => sprintf(t('%s (Current)'), Gdn::session()->User->Name)], $ExistingUsers); } // Pre-populate our ConnectName field with the passed name if we couldn't match it. if (!isset($NameFound) && !$IsPostBack) { $this->Form->setFormValue('ConnectName', $this->Form->getFormValue('Name')); } // Block connecting to an existing user if it's disallowed. if (!$allowConnect) { // Make sure photo of existing user doesn't show on the form. $this->Form->setFormValue("Photo", null); // Ignore existing users found. $ExistingUsers = []; } // Set our final answer on matched users. $this->setData('ExistingUsers', $ExistingUsers); // Validate our email address if we have one. if (UserModel::noEmail()) { $emailValid = true; } else { $emailValid = validateRequired($this->Form->getFormValue('Email')); } // Set some nice variable names for logic clarity. $noMatches = !is_array($ExistingUsers) || count($ExistingUsers) == 0; $didNotPickUser = !$UserSelect || $UserSelect == 'other'; $haveName = $this->Form->getFormValue('Name'); // Should we create a new user? if ($didNotPickUser && $haveName && $emailValid && $noMatches) { // Create the user. $User = $this->Form->formValues(); $User['Password'] = randomString(16); // Required field. $User['HashMethod'] = 'Random'; $User['Source'] = $this->Form->getFormValue('Provider'); $User['SourceID'] = $this->Form->getFormValue('UniqueID'); $User['Attributes'] = $this->Form->getFormValue('Attributes', null); $User['Email'] = $this->Form->getFormValue('ConnectEmail', $this->Form->getFormValue('Email', null)); $User['Name'] = $this->Form->getFormValue('ConnectName', $this->Form->getFormValue('Name', null)); $UserID = $UserModel->register($User, ['CheckCaptcha' => false, 'ValidateEmail' => false, 'NoConfirmEmail' => true, 'SaveRoles' => $SaveRolesRegister]); $User['UserID'] = $UserID; $this->EventArguments['UserID'] = $UserID; $this->fireEvent('AfterConnectSave'); $this->Form->setValidationResults($UserModel->validationResults()); // Save the association to the new user. if ($UserID) { $UserModel->saveAuthentication(['UserID' => $UserID, 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID')]); $this->Form->setFormValue('UserID', $UserID); $this->Form->setFormValue('UserSelect', false); // Sign in as the new user. Gdn::session()->start($UserID, true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // Send the welcome email. if (c('Garden.Registration.SendConnectEmail', false)) { try { $providerName = $this->Form->getFormValue('ProviderName', $this->Form->getFormValue('Provider', 'Unknown')); $UserModel->sendWelcomeEmail($UserID, '', 'Connect', ['ProviderName' => $providerName]); } catch (Exception $Ex) { // Do nothing if emailing doesn't work. } } // Move along. $this->_setRedirect(Gdn::request()->get('display') === 'popup'); } } } // Finished our connection logic. // Save the user's choice. if ($IsPostBack) { $PasswordHash = new Gdn_PasswordHash(); if (!$UserSelect || $UserSelect == 'other') { // The user entered a username. Validate it. $ConnectNameEntered = true; if ($this->Form->validateRule('ConnectName', 'ValidateRequired')) { $ConnectName = $this->Form->getFormValue('ConnectName'); $User = false; if (c('Garden.Registration.NameUnique')) { // Check to see if there is already a user with the given name. $User = $UserModel->getWhere(array('Name' => $ConnectName))->firstRow(DATASET_TYPE_ARRAY); } if (!$User) { // Using a new username, so validate it. $this->Form->validateRule('ConnectName', 'ValidateUsername'); } } } else { // The user selected an existing user. $ConnectNameEntered = false; if ($UserSelect == 'current') { if (Gdn::session()->UserID == 0) { // This should never happen, but a user could click submit on a stale form. $this->Form->addError('@You were unexpectedly signed out.'); } else { $UserSelect = Gdn::session()->UserID; } } $User = $UserModel->getID($UserSelect, DATASET_TYPE_ARRAY); } // End user selection. if (isset($User) && $User) { // Make sure the user authenticates. if (!$User['UserID'] == Gdn::session()->UserID && $allowConnect) { $hasPassword = $this->Form->validateRule('ConnectPassword', 'ValidateRequired', sprintf(t('ValidateRequired'), t('Password'))); if ($hasPassword) { // Validate their password. try { $password = $this->Form->getFormValue('ConnectPassword'); $name = $this->Form->getFormValue('ConnectName'); if (!$PasswordHash->checkPassword($password, $User['Password'], $User['HashMethod'], $name)) { if ($ConnectNameEntered) { $this->Form->addError('The username you entered has already been taken.'); } else { $this->Form->addError('The password you entered is incorrect.'); } } } catch (Gdn_UserException $Ex) { $this->Form->addError($Ex); } } } } elseif ($this->Form->errorCount() == 0) { // The user doesn't exist so we need to add another user. $User = $this->Form->formValues(); $User['Name'] = $User['ConnectName']; $User['Password'] = randomString(16); // Required field. $User['HashMethod'] = 'Random'; $UserID = $UserModel->register($User, ['CheckCaptcha' => false, 'NoConfirmEmail' => true, 'SaveRoles' => $SaveRolesRegister]); $User['UserID'] = $UserID; $this->EventArguments['UserID'] = $UserID; $this->fireEvent('AfterConnectSave'); $this->Form->setValidationResults($UserModel->validationResults()); // Send the welcome email. if ($UserID && c('Garden.Registration.SendConnectEmail', false)) { $providerName = $this->Form->getFormValue('ProviderName', $this->Form->getFormValue('Provider', 'Unknown')); $UserModel->sendWelcomeEmail($UserID, '', 'Connect', ['ProviderName' => $providerName]); } } // Save the user authentication association. if ($this->Form->errorCount() == 0) { if (isset($User) && val('UserID', $User)) { $UserModel->saveAuthentication(['UserID' => $User['UserID'], 'Provider' => $this->Form->getFormValue('Provider'), 'UniqueID' => $this->Form->getFormValue('UniqueID')]); $this->Form->setFormValue('UserID', $User['UserID']); } // Sign the user in. Gdn::session()->start($this->Form->getFormValue('UserID'), true, (bool) $this->Form->getFormValue('RememberMe', true)); Gdn::userModel()->fireEvent('AfterSignIn'); // Move along. $this->_setRedirect(Gdn::request()->get('display') === 'popup'); } } // End of user choice processing. $this->render(); }