/**
* 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();
}