/** * Form handler for the rest of the user system, (auth handler has already been executed). * * @param \Form $form * * @return bool|string */ public static function RegisterHandler(\Form $form){ /////// VALIDATION \\\\\\\\ // All other validation can be done from the model. // All set calls will throw a ModelValidationException if the validation fails. try{ /** @var \UserModel $user */ $user = $form->getElement('user')->get('value'); // setFromForm will handle all attributes and custom values. $user->setFromForm($form); } catch(\ModelValidationException $e){ // Make a note of this! \SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage()); \Core\set_message($e->getMessage(), 'error'); return false; } catch(\Exception $e){ // Make a note of this! \SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage()); if(DEVELOPMENT_MODE){ \Core\set_message($e->getMessage(), 'error'); } else{ \Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION'); } return false; } if( \Core\user()->checkAccess('g:admin') ) { $active = ($form->getElementValue('active') === "on" ? 1 : 0); $user->set('active', $active); } else { $user->setDefaultActiveStatuses(); } $user->setDefaultGroups(); $user->setDefaultMetaFields(); $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(); 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; } return $url; } // It was created administratively; redirect there instead. else{ \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); return '/user/admin'; } }
public function wpadmin() { $view = $this->getView(); $request = $this->getPageRequest(); if ($request->isPost()) { // Did they actually try to submit this form?...... silly bot ;) SystemLogModel::LogSecurityEvent('/wp-admin Honeypot POST', 'POST submission to /wp-admin detected!', print_r($_POST, true)); $expireback = new CoreDateTime(); $expireback->modify('+2 days'); $block = IpBlacklistModel::Find(['ip_addr = ' . REMOTE_IP . '/32'], 1); if (!$block) { $block = new IpBlacklistModel(); $block->set('ip_addr', REMOTE_IP . '/32'); } $block->setFromArray(['expires' => $expireback->getFormatted('U', Time::TIMEZONE_GMT), 'message' => 'You tried to submit a wp-admin page.... this is not a WP site!', 'comment' => 'Bot or user submitted to wp-admin']); $block->save(); } else { // Just record the hit. SystemLogModel::LogSecurityEvent('/wp-admin Honeypot GET', 'GET request to /wp-admin detected!'); } $view->templatename = 'pages/wphoneypot/wpadmin.phtml'; $view->mastertemplate = false; }
/** * Check permissions on the user and system and return either blank or a string containing the error. * * @param string $step * * @return array|null */ private function _checkPermissions($step) { $error = null; if (!\ConfigHandler::Get('/package_repository/base_directory')) { // Check if the config is even set, can't proceed if it's not. trigger_error('The package repository does not appear to be setup yet. Please browse to Configuration and the appropriate options.'); return ['status' => View::ERROR_SERVERERROR, 'message' => 'The package repository is not setup on this server.']; } $dir = Factory::Directory(\ConfigHandler::Get('/package_repository/base_directory')); if (!$dir->exists()) { trigger_error($dir->getPath() . ' does not appear to exist! Unable to browse repo.xml without it.'); return ['status' => View::ERROR_SERVERERROR, 'message' => $dir->getPath() . ' does not seem to exist!']; } elseif (!$dir->isReadable()) { trigger_error($dir->getPath() . ' does not appear to be readable! Unable to browse repo.xml without it.'); return ['status' => View::ERROR_SERVERERROR, 'message' => $dir->getPath() . ' does not seem to be readable!']; } if (ConfigHandler::Get('/package_repository/is_private')) { // Lookup this license key, (or request one if not present). $valid = false; $autherror = 'Access to ' . SITENAME . ' (Package Repository) requires a license key and password.'; if (isset($_SERVER['PHP_AUTH_PW']) && isset($_SERVER['PHP_AUTH_USER'])) { $user = $_SERVER['PHP_AUTH_USER']; $pw = $_SERVER['PHP_AUTH_PW']; } else { $user = $pw = null; } if ($user && $pw) { /** @var PackageRepositoryLicenseModel $license */ $license = PackageRepositoryLicenseModel::Construct($user); $licvalid = $license->isValid($pw); if ($licvalid == 0) { // Lock this license to the remote IP, if requested by the admin. if (ConfigHandler::Get('/package_repository/auto_ip_restrict') && !$license->get('ip_restriction')) { $license->set('ip_restriction', REMOTE_IP); $license->save(); } SystemLogModel::LogInfoEvent('/packagerepository/' . $step, '[' . $user . '] accessed repository successfully'); return null; } else { if (($licvalid & PackageRepositoryLicenseModel::VALID_PASSWORD) == PackageRepositoryLicenseModel::VALID_PASSWORD) { $autherror = '[' . $user . '] Invalid license password'; $status = View::ERROR_ACCESSDENIED; SystemLogModel::LogSecurityEvent('/packagerepository/password_failure', $autherror); } if (($licvalid & PackageRepositoryLicenseModel::VALID_ACCESS) == PackageRepositoryLicenseModel::VALID_ACCESS) { $autherror = '[' . $user . '] IP address not authorized'; $status = View::ERROR_ACCESSDENIED; SystemLogModel::LogSecurityEvent('/packagerepository/ip_restriction', $autherror); } if (($licvalid & PackageRepositoryLicenseModel::VALID_EXPIRED) == PackageRepositoryLicenseModel::VALID_EXPIRED) { $autherror = '[' . $user . '] License provided has expired, please request a new one.'; $status = View::ERROR_GONE; SystemLogModel::LogSecurityEvent('/packagerepository/expired_license', $autherror); } if (($licvalid & PackageRepositoryLicenseModel::VALID_INVALID) == PackageRepositoryLicenseModel::VALID_INVALID) { $autherror = '[' . $user . '] License does not exist'; $status = View::ERROR_EXPECTATIONFAILED; SystemLogModel::LogSecurityEvent('/packagerepository/invalid_license', $autherror); } return ['status' => $status, 'message' => $autherror]; } } if (!$valid) { header('WWW-Authenticate: Basic realm="' . SITENAME . ' (Package Repository)"'); header('HTTP/1.0 401 Unauthorized'); echo $autherror; exit; } } else { SystemLogModel::LogInfoEvent('/packagerepository/' . $step, '[anonymous connection] accessed repository successfully'); return null; } }
/** * Helper function called by the *_install views. * * @param $type * @param $name * @param $version */ private function _performInstall($type, $name, $version){ $view = $this->getView(); $req = $this->getPageRequest(); $dryrun = $req->getParameter('dryrun'); $verbose = $req->getParameter('verbose'); $nl = "<br/>\n"; // For standard calls, this is a json-only page. // verbose runs are html however. if($verbose){ $view->contenttype = View::CTYPE_HTML; $view->mode = View::MODE_NOOUTPUT; } else{ $view->contenttype = View::CTYPE_JSON; } // This is a post-only page! if(!$req->isPost()){ $view->error = View::ERROR_BADREQUEST; return; } $return = UpdaterHelper::PerformInstall($type, $name, $version, $dryrun, $verbose); // If it's not a dry run, record a log of this action! if(!$dryrun){ if($return['status']){ $logmsg = 'Installation of ' . $type . ' ' . $name . ' ' . $version . ' succeeded!' . "\n" . $return['message']; if(isset($return['changes'])){ foreach($return['changes'] as $change){ $logmsg .= "\n" . $change; } } $logstatus = 'success'; } else{ $logmsg = 'Installation of ' . $type . ' ' . $name . ' ' . $version . ' failed due to' . "\n" . $return['message']; $logstatus = 'fail'; } SystemLogModel::LogSecurityEvent( '/updater/installation', $logmsg ); } if($verbose){ if(!$return['status']){ echo $nl . '[=========== RESULTS ===========]' . $nl; echo '[ERROR] - ' . $return['message'] . $nl; } else{ echo $nl . '[=========== RESULTS ===========]' . $nl; if(isset($return['changes'])){ foreach($return['changes'] as $change){ echo '[INFO] - ' . $change . $nl; } } echo '[SUCCESS] - Performed all operations successfully!' . $nl; } echo '<div id="results" style="display:none;" status="' . $return['status'] . '">' . $return['message'] . '</div>'; } else{ $view->jsondata = $return; } }
/** * This will check and see how many 404 requests there have been recently. * * @return bool */ public static function Check404Pages() { // How long back do I want to check the logs? $time = new DateTime(); $time->modify('-30 seconds'); $ds = Dataset::Init()->table('user_activity')->where(['status = 404', 'ip_addr = ' . REMOTE_IP, 'datetime > ' . $time->format('U')])->count()->execute(); if ($ds->num_rows > 30) { // CHILL THAR F****R! $time->modify('+6 minutes'); $blacklist = new \IpBlacklistModel(); $blacklist->setFromArray(['ip_addr' => REMOTE_IP . '/24', 'expires' => $time->format('U'), 'message' => 'You have requested too many "404" pages recently, please go get some coffee and wait for a short bit. If you are a bot and/or spammer, please bugger off.', 'comment' => '5-minute auto-ban for too many 404 requests in 30 seconds']); $blacklist->save(); \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to too many 404 requests in 30 seconds.'); die($blacklist->get('message')); } }
/** * Check the user's IP in the blacklist and see if it's found. * * If it is and has a high enough submission rate, (in a 24 hour period), then block the user completely and immediately. */ public static function CheckIP() { $record = \sfsBlacklistModel::Construct(REMOTE_IP); // It's not in there, YAY! if (!$record->exists()) { return; } // Is the submission score high enough? $highscore = 100; if ($record->get('submissions') > $highscore) { // YOU can haz good party tiem nau \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $highscore . ' submissions to sfs in a 24 hour period.'); die('IP Blocked due to high spam score'); } // Submissions listed, but not exceedingly high? $warnlevel = 5; if ($record->get('submissions') > $warnlevel) { if (\Core\Session::Get('security_antispam_allowed') === null) { $html = '<html><body>'; $html .= '<!-- You smell of spam.... are you sure you didn\'t come from a can?-->'; if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['happyfuntime']) && \Core\Session::Get('happyfuntimecheck')) { // It's an attempt! if ($_POST['happyfuntime'] == \Core\Session::Get('happyfuntimecheck')) { \SystemLogModel::LogSecurityEvent('/security/unblocked', 'User successfully answered an anti-bot math question, unblocking.'); \Core\Session::Set('security_antispam_allowed', true); } else { \SystemLogModel::LogSecurityEvent('/security/captchafailed', 'User attempted, but failed in answering an anti-bot math question.'); $html .= '<b>NOPE!</b>'; } } \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $warnlevel . ' submissions to sfs in a 24 hour period.'); $random1 = rand(4, 6) * 2; $random2 = rand(1, 3) * 2; $random3 = rand(1, 2); switch ($random3) { case 1: $result = $random1 / $random2; $operation = 'divided by'; break; case 2: $result = $random1 * $random2; $operation = 'multiplied by'; break; } \Core\Session::Set('happyfuntimecheck', $result); switch ($random2) { case 1: $random2 = 'oNe'; break; case 2: $random2 = 'Tw0'; break; case 3: $random2 = 'ThRe'; break; case 4: $random2 = 'Foor'; break; case 5: $random2 = 'fIve'; break; case 6: $random2 = 'Siix'; break; } $html .= '<form method="POST"><p>What is ' . $random1 . ' ' . $operation . ' ' . $random2 . '?</p><input type="text" name="happyfuntime" size="3"/><input type="submit" value="GO"/></form></body></html>'; die($html); } } }
/** * Shortcut function to record a new security log entry. * * Return the log entry that's created. * * @param string $action * @param null|string $status * @param null|int $affecteduser * @param null|string $details * * @return null */ public static function Log($action, $status = null, $affecteduser = null, $details = null) { // The security log has been removed! trigger_error('SecurityLogModel::Log has been deprecated, please use SystemLogModel::LogSecurityEvent() instead.', E_USER_DEPRECATED); SystemLogModel::LogSecurityEvent($action, $details, null, $affecteduser); }
/** * Form Handler for logging in. * * @static * * @param \Form $form * * @return bool|null|string */ public static function LoginHandler(\Form $form){ /** @var \FormElement $e */ $e = $form->getElement('email'); /** @var \FormElement $p */ $p = $form->getElement('pass'); /** @var \UserModel $u */ $u = \UserModel::Find(array('email' => $e->get('value')), 1); if(!$u){ // Log this as a login attempt! $logmsg = 'Failed Login. Email not registered' . "\n" . 'Email: ' . $e->get('value') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND'); return false; } if($u->get('active') == 0){ // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id')); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_NOT_ACTIVE'); return false; } elseif($u->get('active') == -1){ // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login after account deactivation.' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id')); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_DEACTIVATED'); return false; } try{ /** @var \Core\User\AuthDrivers\datastore $auth */ $auth = $u->getAuthDriver('datastore'); } catch(Exception $e){ $e->setError('t:MESSAGE_ERROR_USER_LOGIN_PASSWORD_AUTH_DISABLED'); return false; } // This is a special case if the password isn't set yet. // It can happen with imported users or if a password is invalidated. if($u->get('password') == ''){ // Use the Nonce system to generate a one-time key with this user's data. $nonce = \NonceModel::Generate( '20 minutes', ['type' => 'password-reset', 'user' => $u->get('id')] ); $link = '/datastoreauth/forgotpassword?e=' . urlencode($u->get('email')) . '&n=' . $nonce; $email = new \Email(); $email->setSubject('Initial Password Request'); $email->to($u->get('email')); $email->assign('link', \Core\resolve_link($link)); $email->assign('ip', REMOTE_IP); $email->templatename = 'emails/user/initialpassword.tpl'; try{ $email->send(); \SystemLogModel::LogSecurityEvent('/user/initialpassword/send', 'Initial password request sent successfully', null, $u->get('id')); \Core\set_message('t:MESSAGE_INFO_USER_LOGIN_MUST_SET_NEW_PASSWORD_INSTRUCTIONS_HAVE_BEEN_EMAILED'); return true; } catch(\Exception $e){ \Core\ErrorManagement\exception_handler($e); \Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_MUST_SET_NEW_PASSWORD_UNABLE_TO_SEND_EMAIL'); return false; } } if(!$auth->checkPassword($p->get('value'))){ // Log this as a login attempt! $logmsg = 'Failed Login. Invalid password' . "\n" . 'Email: ' . $e->get('value') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login/failed_password', $logmsg, null, $u->get('id')); // Also, I want to look up and see how many login attempts there have been in the past couple minutes. // If there are too many, I need to start slowing the attempts. $time = new \CoreDateTime(); $time->modify('-5 minutes'); $securityfactory = new \ModelFactory('SystemLogModel'); $securityfactory->where('code = /user/login/failed_password'); $securityfactory->where('datetime > ' . $time->getFormatted(\Time::FORMAT_EPOCH, \Time::TIMEZONE_GMT)); $securityfactory->where('ip_addr = ' . REMOTE_IP); $attempts = $securityfactory->count(); if($attempts > 4){ // Start slowing down the response. This should help deter brute force attempts. // (x+((x-7)/4)^3)-4 sleep( ($attempts+(($attempts-7)/4)^3)-4 ); // This makes a nice little curve with the following delays: // 5th attempt: 0.85 // 6th attempt: 2.05 // 7th attempt: 3.02 // 8th attempt: 4.05 // 9th attempt: 5.15 // 10th attempt: 6.52 // 11th attempt: 8.10 // 12th attempt: 10.05 } $e->setError('t:MESSAGE_ERROR_USER_LOGIN_INCORRECT_PASSWORD'); $p->set('value', ''); return false; } if($form->getElementValue('redirect')){ // The page was set via client-side javascript on the login page. // This is the most reliable option. $url = $form->getElementValue('redirect'); } elseif(REL_REQUEST_PATH == '/user/login'){ // If the user came from the registration page, get the page before that. $url = $form->referrer; } else{ // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } // Well, record this too! \SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via password)', null, $u->get('id')); // yay... $u->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $u->save(); \Core\Session::SetUser($u); // Allow an external script to override the redirecting URL. $overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'); if($overrideurl){ $url = $overrideurl; } return $url; }
public function preview() { $view = $this->getView(); $request = $this->getPageRequest(); // This is designed to only return data! $view->mode = View::MODE_NOOUTPUT; // And it's going to be an image of some sorts. // This will get overridden by the actual image displaying method. $view->contenttype = 'image/png'; // And it shouldn't be recorded in navigation. $view->record = false; // The filename should be something like files/public/blah... or files/assets/foo... // This will get fed into the core system and checked internally. $filename = $request->getParameter('f'); // Was there a resize-to dimension requested? $d = $request->getParameter('d'); // The inbound filename must be in the format of base64:[b64data]. if (strpos($filename, 'base64:') !== 0) { if(DEVELOPMENT_MODE){ error_log('Invalid request made for /file/preview! Expecting: [base64:*b64data*], Received: [' . $filename . ']'); } $file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png'); $file->displayPreview($d); return; } // This preview ONLY supports assets and public files! // This is a security precaution. $base = base64_decode(substr($filename, 7)); if(!(strpos($base, 'public/') === 0 || strpos($base, 'assets/') === 0 || strpos($base, 'asset/') === 0)){ SystemLogModel::LogSecurityEvent('/file/preview', 'Invalid file requested: ' . $base); if(DEVELOPMENT_MODE){ error_log('Invalid request made for /file/preview! Expecting: [public/* or asset[s]/*], Received: [' . $base . ']'); } $file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png'); $file->displayPreview($d); return; } $file = \Core\Filestore\Factory::File($filename); if(!$file->exists()){ if(DEVELOPMENT_MODE){ error_log('File not found for /file/preview! Looking For: [' . $file->getFilename('') . ' (' . $filename . ') ]'); } $file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png'); $file->displayPreview($d); return; } // And this will render it to the browser. $file->displayPreview($d); }
/** * View to accept and process the FB login post. * * This will redirect to the registration page if the user doesn't exist, * will throw an error and display a link to enable FB if it's not enabled already, * or will simply log the user in via Facebook and sync his/her settings. */ public function login() { $view = $this->getView(); $request = $this->getPageRequest(); $view->ssl = true; $view->record = false; $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 (!$request->isPost()) { return View::ERROR_BADREQUEST; } $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; } /** @var \UserModel|null $user */ $user = UserModel::Find(['email' => $user_profile['email']], 1); if (!$user) { if (ConfigHandler::Get('/user/register/allowpublic')) { // If public registration is enabled, then redirect the user to the registration page to complete their registration. $user = new UserModel(); $user->set('email', $user_profile['email']); $user->enableAuthDriver('facebook'); $user->disableAuthDriver('datastore'); /** @var \Facebook\UserAuth $auth */ $auth = $user->getAuthDriver('facebook'); $auth->syncUser($_POST['access-token']); // Otherwise, w00t! Record this user into a nonce and forward to step 2 of registration. $nonce = NonceModel::Generate('20 minutes', null, ['user' => $user, 'redirect' => $_POST['redirect']]); \Core\redirect('/user/register2/' . $nonce); } else { // Log this as a login attempt! $logmsg = 'Failed Login (Facebook). Email not registered' . "\n" . 'Email: ' . $user_profile['email'] . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg); \Core\set_message('Your Facebook email (' . $user_profile['email'] . ') does not appear to be registered on this site.', 'error'); \Core\go_back(); return null; } } elseif (!$user->get('active')) { // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $user->get('id')); \Core\set_message('Your account is not active yet.', 'error'); \Core\go_back(); return null; } try { /** @var \Facebook\UserAuth $auth */ $auth = $user->getAuthDriver('facebook'); } catch (Exception $e) { \Core\set_message('Your account does not have Facebook logins enabled! <a href="' . \Core\resolve_link('/facebook/enable') . '">Do you want to enable Facebook?</a>', 'error'); \Core\go_back(); return null; } if (!$user->isActive()) { \Core\set_message('Your account is not active!', 'error'); \Core\go_back(); return null; } // Well yay the user is available and authencation driver is ready! $auth->syncUser($_POST['access-token']); if ($_POST['redirect']) { // The page was set via client-side javascript on the login page. // This is the most reliable option. $url = $_POST['redirect']; } elseif (REL_REQUEST_PATH == '/facebook/login') { // 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; } // Well, record this too! \SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via Facebook)', null, $user->get('id')); // yay... $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); \Core\Session::SetUser($user); // Allow an external script to override the redirecting URL. $overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'); if ($overrideurl) { $url = $overrideurl; } \Core\redirect($url); } else { \Core\go_back(); } }
/** * 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); }
/** * 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; }
/** * Get the current user model that is logged in. * * To support legacy systems, this will also return the User object if it's available instead. * This support is for < 2.8.x Core installations and will be removed after some amount of time TBD. * * If no user systems are currently available, null is returned. * * @return \UserModel */ function user(){ static $_CurrentUserAccount = null; if(!class_exists('\\UserModel')){ return null; } if($_CurrentUserAccount !== null){ // Cache this for the page load. return $_CurrentUserAccount; } if(isset($_SERVER['HTTP_X_CORE_AUTH_KEY'])){ // Allow an auth key to be used to authentication the requested user instead! $user = \UserModel::Find(['apikey = ' . $_SERVER['HTTP_X_CORE_AUTH_KEY']], 1); if($user){ $_CurrentUserAccount = $user; } } elseif(Session::Get('user') instanceof \UserModel){ // There is a valid user account in the session! // But check if this user is forced to be resynced first. if(isset(Session::$Externals['user_forcesync'])){ // A force sync was requested by something that modified the original UserModel object. // Keep the user logged in, but reload the data from the database. $_CurrentUserAccount = \UserModel::Construct(Session::Get('user')->get('id')); // And cache this updated user model back to the session. Session::Set('user', $_CurrentUserAccount); unset(Session::$Externals['user_forcesync']); } else{ $_CurrentUserAccount = Session::Get('user'); } } if($_CurrentUserAccount === null){ // No valid user found. $_CurrentUserAccount = new \UserModel(); } // If this is in multisite mode, blank out the access string cache too! // This is because siteA may have some groups, while siteB may have another. // We don't want a user going to a site they have full access to, hopping to another and having cached permissions! if(\Core::IsComponentAvailable('multisite') && class_exists('MultiSiteHelper') && \MultiSiteHelper::IsEnabled()){ $_CurrentUserAccount->clearAccessStringCache(); } // Did this user request sudo access for another user? if(Session::Get('user_sudo') !== null){ $sudo = Session::Get('user_sudo'); if($sudo instanceof \UserModel){ // It's a valid user! if($_CurrentUserAccount->checkAccess('p:/user/users/sudo')){ // This user can SUDO! // (only if the other user is < SA or current == SA). if($sudo->checkAccess('g:admin') && !$_CurrentUserAccount->checkAccess('g:admin')){ Session::UnsetKey('user_sudo'); \SystemLogModel::LogSecurityEvent('/user/sudo', 'Authorized but non-SA user requested sudo access to a system admin!', null, $sudo->get('id')); } else{ // Ok, everything is good. // Remap the current user over to this sudo'd account! $_CurrentUserAccount = $sudo; } } else{ // This user can NOT sudo!!! Session::UnsetKey('user_sudo'); \SystemLogModel::LogSecurityEvent('/user/sudo', 'Unauthorized user requested sudo access to another user!', null, $sudo->get('id')); } } else{ Session::UnsetKey('user_sudo'); } } return $_CurrentUserAccount; }