Example #1
0
	/**
	 * 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();
     }
 }
Example #11
0
	/**
	 * 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);
	}
Example #12
0
	/**
	 * 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;
	}
Example #13
0
/**
 * 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;
}