private function _forgotPassword2(){ $view = $this->getView(); $request = $this->getPageRequest(); $genericauth = new \Core\User\AuthDrivers\datastore(); // Create a simple form to render. This is better than doing it in the template. $form = new Form(); $form->set('method', 'POST'); $form->addElement('password', ['name' => 'p1', 'title' => 'Password', 'required' => true]); $form->addElement('password', ['name' => 'p2', 'title' => 'Confirm', 'required' => true]); $form->addElement('submit', ['name' => 'submit', 'value' => 'Set New Password']); $view->title = 'Forgot Password'; $view->assign('step', 2); $view->assign('form', $form); $view->assign('requirements', $genericauth->getPasswordComplexityAsHTML()); $n = $request->getParameter(0); /** @var $nonce NonceModel */ $nonce = NonceModel::Construct($n); if(!$nonce->isValid()){ SystemLogModel::LogSecurityEvent('/user/forgotpassword/confirm', 'Failed Forgot Password. Invalid nonce requested: [' . $n . ']'); \Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND'); \core\redirect('/'); return; } $nonce->decryptData(); $data = $nonce->get('data'); /** @var UserModel $u */ $u = UserModel::Construct($data['user']); if(!$u){ SystemLogModel::LogSecurityEvent('/user/forgotpassword/confirm', 'Failed Forgot Password. Invalid user account requested: [' . $data['user'] . ']'); \Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND'); \core\redirect('/'); return; } if($request->isPost()){ // Validate the password. if($_POST['p1'] != $_POST['p2']){ \Core\set_message('t:MESSAGE_ERROR_USER_REGISTER_PASSWORD_MISMATCH'); return; } // Else, try to set it... the user model will complain if it's invalid. try{ $u->enableAuthDriver('datastore'); /** @var \Core\User\AuthDrivers\datastore $auth */ $auth = $u->getAuthDriver('datastore'); $auth->setPassword($_POST['p1']); $u->save(); // NOW I can invalidate that nonce! $nonce->markUsed(); SystemLogModel::LogSecurityEvent('/user/forgotpassword/confirm', 'Reset password successfully!', null, $u->get('id')); \Core\set_message('Reset password successfully', 'success'); if($u->get('active')){ \Core\Session::SetUser($u); } \core\redirect('/'); } catch(ModelValidationException $e){ SystemLogModel::LogSecurityEvent('/user/forgotpassword/confirm', 'Failed Forgot Password. ' . $e->getMessage(), null, $u->get('id')); \Core\set_message($e->getMessage(), 'error'); return; } catch(Exception $e){ SystemLogModel::LogSecurityEvent('/user/forgotpassword/confirm', 'Failed Forgot Password. ' . $e->getMessage(), null, $u->get('id')); \Core\set_message((DEVELOPMENT_MODE ? $e->getMessage() : 'An unknown error occured'), 'error'); return; } } }
/** * Shorthand function to validate and "mark as used" a Nonce key. * @param string $key The nonce key to validate * @param string|null|mixed $hash An optional hash to validate also * * @return boolean */ public static function ValidateAndUse($key, $hash = null) { /** @var $nonce NonceModel */ $nonce = NonceModel::Construct(strtolower($key)); if ($nonce->isValid($hash)) { $nonce->markUsed(); return true; } else { return false; } }
/** * The actual Core registration page. * * This renders all the user's configurable options at registration. */ public function register2(){ $view = $this->getView(); $request = $this->getPageRequest(); $manager = \Core\user()->checkAccess('p:/user/users/manage'); // Current user an admin? // Anonymous users should have access to this if it's allow public. if(!\Core\user()->exists() && !ConfigHandler::Get('/user/register/allowpublic')){ return View::ERROR_BADREQUEST; } // Authenticated users must check the permission to manage users. if(\Core\user()->exists() && !$manager){ return View::ERROR_ACCESSDENIED; } /** @var NonceModel $nonce */ $nonce = NonceModel::Construct($request->getParameter(0)); if(!$nonce->isValid()){ \Core\set_message('Invalid nonce token, please try again.', 'error'); \Core\go_back(); } $nonce->decryptData(); $data = $nonce->get('data'); if(!isset($data['user']) || !($data['user'] instanceof UserModel)){ if(DEVELOPMENT_MODE){ \Core\set_message('Your nonce does not include a "user" key. Please ensure that this is set to a non-existent UserModel object!', 'error'); } else{ \Core\set_message('Invalid login type, please try again later.', 'error'); } \Core\go_back(); } /** @var UserModel $user */ $user = $data['user']; $form = \Core\User\Helper::GetForm($user); // If the total number of form elements here are only 2, then only the user object and submit button are present. // Instead of showing the form, auto-submit to that destination. if(sizeof($form->getElements()) <= 2){ $user->setDefaultGroups(); $user->setDefaultMetaFields(); $user->setDefaultActiveStatuses(); $user->generateNewApiKey(); $user->save(); // User created... make a log of this! \SystemLogModel::LogSecurityEvent('/user/register', 'User registration successful', null, $user->get('id')); // Send a thank you for registering email to the user. try{ $user->sendWelcomeEmail(); } catch(\Exception $e){ \Core\ErrorManagement\exception_handler($e); \Core\set_message('t:MESSAGE_ERROR_CANNOT_SEND_WELCOME_EMAIL'); } // "login" this user if not already logged in. if(!\Core\user()->exists()){ if($user->get('active')){ $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); \Core\Session::SetUser($user); } \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); if(($overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'))){ // Allow an external script to override the redirecting URL. $url = $overrideurl; } elseif($form->getElementValue('redirect')){ // The preferred default redirect method. // This is set from /user/register2, which is in turn passed in, (hopefully), by the original callee registration page. $url = $form->getElementValue('redirect'); } elseif(strpos(REL_REQUEST_PATH, '/user/register') === 0){ // If the user came from the registration page, get the page before that. $url = '/'; } else{ // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } \Core\redirect($url); } // It was created administratively; redirect there instead. else{ \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); \Core\redirect('/user/admin'); } } $form->addElement('hidden', ['name' => 'redirect', 'value' => $data['redirect']]); $view->title = 'Complete Registration'; $view->assign('form', $form); }
/** * Page to enable Facebook logins for user accounts. * * @return int|null|string */ public function enable() { $request = $this->getPageRequest(); $auths = \Core\User\Helper::GetEnabledAuthDrivers(); if (!isset($auths['facebook'])) { // Facebook isn't enabled, simply redirect to the home page. \Core\redirect('/'); } if (!FACEBOOK_APP_ID) { \Core\redirect('/'); } if (!FACEBOOK_APP_SECRET) { \Core\redirect('/'); } // If it was a POST, then it should be the first page. if ($request->isPost()) { $facebook = new Facebook(['appId' => FACEBOOK_APP_ID, 'secret' => FACEBOOK_APP_SECRET]); // Did the user submit the facebook login request? if (isset($_POST['login-method']) && $_POST['login-method'] == 'facebook' && $_POST['access-token']) { try { $facebook->setAccessToken($_POST['access-token']); /** @var int $fbid The user ID from facebook */ $fbid = $facebook->getUser(); /** @var array $user_profile The array of user data from Facebook */ $user_profile = $facebook->api('/me'); } catch (Exception $e) { \Core\set_message($e->getMessage(), 'error'); \Core\go_back(); return null; } // If the user is logged in, then the verification logic is slightly different. if (\Core\user()->exists()) { // Logged in users, the email must match. if (\Core\user()->get('email') != $user_profile['email']) { \Core\set_message('Your Facebook email is ' . $user_profile['email'] . ', which does not match your account email! Unable to link accounts.', 'error'); \Core\go_back(); return null; } $user = \Core\user(); } else { /** @var \UserModel|null $user */ $user = UserModel::Find(['email' => $user_profile['email']], 1); if (!$user) { \Core\set_message('No local account found with the email ' . $user_profile['email'] . ', please <a href="' . \Core\resolve_link('/user/register') . '"create an account</a> instead.', 'error'); \Core\go_back(); return null; } } // Send an email with a nonce link that will do the actual activation. // This is a security feature so just anyone can't link another user's account. $nonce = NonceModel::Generate('20 minutes', null, ['user' => $user, 'access_token' => $_POST['access-token']]); $email = new Email(); $email->to($user->get('email')); $email->setSubject('Facebook Activation Request'); $email->templatename = 'emails/facebook/enable_confirmation.tpl'; $email->assign('link', \Core\resolve_link('/facebook/enable/' . $nonce)); if ($email->send()) { \Core\set_message('An email has been sent to your account with a link enclosed. Please click on that to complete activation within twenty minutes.', 'success'); \Core\go_back(); return null; } else { \Core\set_message('Unable to send a confirmation email, please try again later.', 'error'); \Core\go_back(); return null; } } } // If there is a nonce enclosed, then it should be the second confirmation page. // This is the one that actually performs the action. if ($request->getParameter(0)) { /** @var NonceModel $nonce */ $nonce = NonceModel::Construct($request->getParameter(0)); if (!$nonce->isValid()) { \Core\set_message('Invalid key requested.', 'error'); \Core\redirect('/'); return null; } $nonce->decryptData(); $data = $nonce->get('data'); /** @var UserModel $user */ $user = $data['user']; try { $facebook = new Facebook(['appId' => FACEBOOK_APP_ID, 'secret' => FACEBOOK_APP_SECRET]); $facebook->setAccessToken($data['access_token']); $facebook->getUser(); $facebook->api('/me'); } catch (Exception $e) { \Core\set_message($e->getMessage(), 'error'); \Core\redirect('/'); return null; } $user->enableAuthDriver('facebook'); /** @var \Facebook\UserAuth $auth */ $auth = $user->getAuthDriver('facebook'); $auth->syncUser($data['access_token']); \Core\set_message('Linked Facebook successfully!', 'success'); // And log the user in! if (!\Core\user()->exists()) { $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); \Core\Session::SetUser($user); } \Core\redirect('/'); return null; } }
/** * Validate the verification email, part 2 of confirmation. * * @param string $nonce * @param string $signature * * @return bool|string */ public static function ValidateVerificationResponse($nonce, $signature) { /** @var \NonceModel $nonce */ $nonce = \NonceModel::Construct($nonce); if(!$nonce->isValid()){ \SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key (Invalid NONCE)', null); return 'Invalid nonce provided!'; } // Now is where the real fun begins. $nonce->decryptData(); $data = $nonce->get('data'); /** @var \UserModel $user */ $user = \UserModel::Construct($data['user']); $gpg = new \Core\GPG\GPG(); $key = $data['key']; $pubKey = $gpg->getKey($key); try{ $sig = $gpg->verifyDataSignature($signature, $data['sentence']); } catch(\Exception $e){ \SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key ' . $key, null, $user->get('id')); return 'Invalid signature'; } $fpr = str_replace(' ', '', $sig->fingerprint); // Trim spaces. if($key != $fpr && $key != $sig->keyID){ // They must match! \SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key ' . $key, null, $user->get('id')); return 'Invalid signature'; } // Otherwise? $user->enableAuthDriver('gpg'); $user->set('gpgauth_pubkey', $fpr); // Was there a photo attached to this public key? if(sizeof($pubKey->getPhotos()) > 0){ $p = $pubKey->getPhotos(); // I just want the first. /** @var \Core\Filestore\File $p */ $p = $p[0]; $localFile = \Core\Filestore\Factory::File('public/user/avatar/' . $pubKey->fingerprint . '.' . $p->getExtension()); $p->copyTo($localFile); $user->set('avatar', $localFile->getFilename(false)); } $user->save(); $nonce->markUsed(); \SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'Verified key ' . $fpr, null, $user->get('id')); return true; }