2018-10-17 12:19:58 +00:00
< ? php
/**
2021-03-29 06:40:20 +00:00
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
2020-02-08 16:16:42 +00:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2018-10-17 12:19:58 +00:00
*/
2020-09-30 14:53:18 +00:00
namespace Friendica\Security ;
2018-10-17 12:19:58 +00:00
2019-12-03 21:29:37 +00:00
use Exception ;
2019-05-13 05:36:09 +00:00
use Friendica\App ;
2020-01-19 20:29:36 +00:00
use Friendica\Core\Config\IConfig ;
2020-01-19 21:23:44 +00:00
use Friendica\Core\PConfig\IPConfig ;
2019-12-08 21:45:34 +00:00
use Friendica\Core\Hook ;
use Friendica\Core\Session ;
use Friendica\Core\System ;
2019-12-03 21:29:37 +00:00
use Friendica\Database\Database ;
2019-12-03 20:18:26 +00:00
use Friendica\Database\DBA ;
2019-12-16 00:35:26 +00:00
use Friendica\DI ;
2019-12-03 20:18:26 +00:00
use Friendica\Model\User ;
2019-12-03 21:29:37 +00:00
use Friendica\Network\HTTPException ;
2021-01-19 04:32:48 +00:00
use Friendica\Security\TwoFactor\Repository\TrustedBrowser ;
2019-12-03 20:18:26 +00:00
use Friendica\Util\DateTimeFormat ;
use Friendica\Util\Network ;
use Friendica\Util\Strings ;
2019-12-03 21:29:37 +00:00
use LightOpenID ;
2020-01-18 19:59:39 +00:00
use Friendica\Core\L10n ;
2019-12-03 21:29:37 +00:00
use Psr\Log\LoggerInterface ;
2018-10-17 12:19:58 +00:00
/**
2020-09-30 14:53:18 +00:00
* Handle Authentication , Session and Cookies
2019-10-10 23:21:41 +00:00
*/
2019-12-03 21:29:37 +00:00
class Authentication
2018-10-17 12:19:58 +00:00
{
2020-01-19 20:29:36 +00:00
/** @var IConfig */
2019-12-03 21:29:37 +00:00
private $config ;
2019-12-15 23:30:39 +00:00
/** @var App\Mode */
private $mode ;
2019-12-03 21:29:37 +00:00
/** @var App\BaseURL */
private $baseUrl ;
/** @var L10n */
private $l10n ;
/** @var Database */
private $dba ;
/** @var LoggerInterface */
private $logger ;
2019-12-08 21:45:34 +00:00
/** @var User\Cookie */
private $cookie ;
2019-12-09 23:44:56 +00:00
/** @var Session\ISession */
private $session ;
2020-01-19 21:23:44 +00:00
/** @var IPConfig */
2020-01-18 15:50:57 +00:00
private $pConfig ;
2019-12-03 21:29:37 +00:00
/**
* Authentication constructor .
*
2020-01-19 20:29:36 +00:00
* @ param IConfig $config
* @ param App\Mode $mode
* @ param App\BaseURL $baseUrl
* @ param L10n $l10n
* @ param Database $dba
* @ param LoggerInterface $logger
* @ param User\Cookie $cookie
2019-12-09 23:44:56 +00:00
* @ param Session\ISession $session
2020-01-19 21:23:44 +00:00
* @ param IPConfig $pConfig
2019-12-03 21:29:37 +00:00
*/
2020-01-19 21:23:44 +00:00
public function __construct ( IConfig $config , App\Mode $mode , App\BaseURL $baseUrl , L10n $l10n , Database $dba , LoggerInterface $logger , User\Cookie $cookie , Session\ISession $session , IPConfig $pConfig )
2019-12-03 21:29:37 +00:00
{
$this -> config = $config ;
2020-01-18 15:50:57 +00:00
$this -> mode = $mode ;
2019-12-03 21:29:37 +00:00
$this -> baseUrl = $baseUrl ;
$this -> l10n = $l10n ;
$this -> dba = $dba ;
$this -> logger = $logger ;
2020-01-18 15:50:57 +00:00
$this -> cookie = $cookie ;
2019-12-09 23:44:56 +00:00
$this -> session = $session ;
2020-01-18 15:50:57 +00:00
$this -> pConfig = $pConfig ;
2019-12-03 21:29:37 +00:00
}
/**
2020-01-19 06:05:23 +00:00
* Tries to auth the user from the cookie or session
2019-12-03 21:29:37 +00:00
*
* @ param App $a The Friendica Application context
*
* @ throws HttpException\InternalServerErrorException In case of Friendica internal exceptions
* @ throws Exception In case of general exceptions ( like SQL Grammar )
*/
2019-12-08 21:45:34 +00:00
public function withSession ( App $a )
2019-12-03 21:29:37 +00:00
{
2019-12-08 21:45:34 +00:00
// When the "Friendica" cookie is set, take the value to authenticate and renew the cookie.
2021-01-17 22:30:18 +00:00
if ( $this -> cookie -> get ( 'uid' )) {
2019-12-08 21:45:34 +00:00
$user = $this -> dba -> selectFirst (
'user' ,
[],
[
2021-01-17 22:30:18 +00:00
'uid' => $this -> cookie -> get ( 'uid' ),
2019-12-08 21:45:34 +00:00
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2019-12-09 23:44:56 +00:00
if ( $this -> dba -> isResult ( $user )) {
2021-01-17 22:30:18 +00:00
if ( ! $this -> cookie -> comparePrivateDataHash ( $this -> cookie -> get ( 'hash' ),
2019-12-08 21:45:34 +00:00
$user [ 'password' ] ? ? '' ,
2021-01-17 22:30:18 +00:00
$user [ 'prvkey' ] ? ? '' )
) {
$this -> logger -> notice ( " Hash doesn't fit. " , [ 'user' => $this -> cookie -> get ( 'uid' )]);
2019-12-30 02:52:56 +00:00
$this -> session -> clear ();
2020-01-09 00:51:54 +00:00
$this -> cookie -> clear ();
2019-12-08 21:45:34 +00:00
$this -> baseUrl -> redirect ();
}
2019-12-03 21:29:37 +00:00
2019-12-08 21:45:34 +00:00
// Renew the cookie
2021-01-17 22:30:18 +00:00
$this -> cookie -> send ();
2019-12-03 21:29:37 +00:00
2019-12-08 21:45:34 +00:00
// Do the authentification if not done by now
2019-12-09 23:44:56 +00:00
if ( ! $this -> session -> get ( 'authenticated' )) {
2019-12-08 21:45:34 +00:00
$this -> setForUser ( $a , $user );
2019-12-03 21:29:37 +00:00
2019-12-08 21:45:34 +00:00
if ( $this -> config -> get ( 'system' , 'paranoia' )) {
2021-01-17 22:30:18 +00:00
$this -> session -> set ( 'addr' , $this -> cookie -> get ( 'ip' ));
2019-12-03 21:29:37 +00:00
}
}
}
}
2019-12-09 23:44:56 +00:00
if ( $this -> session -> get ( 'authenticated' )) {
if ( $this -> session -> get ( 'visitor_id' ) && ! $this -> session -> get ( 'uid' )) {
2021-07-24 11:49:11 +00:00
$contact = $this -> dba -> selectFirst ( 'contact' , [ 'id' ], [ 'id' => $this -> session -> get ( 'visitor_id' )]);
2019-12-03 21:29:37 +00:00
if ( $this -> dba -> isResult ( $contact )) {
2021-07-24 20:34:07 +00:00
$a -> setContactId ( $contact [ 'id' ]);
2019-12-03 21:29:37 +00:00
}
}
2019-12-09 23:44:56 +00:00
if ( $this -> session -> get ( 'uid' )) {
2019-12-03 21:29:37 +00:00
// already logged in user returning
$check = $this -> config -> get ( 'system' , 'paranoia' );
// extra paranoia - if the IP changed, log them out
2019-12-09 23:44:56 +00:00
if ( $check && ( $this -> session -> get ( 'addr' ) != $_SERVER [ 'REMOTE_ADDR' ])) {
2019-12-03 21:29:37 +00:00
$this -> logger -> notice ( 'Session address changed. Paranoid setting in effect, blocking session. ' , [
2019-12-09 23:44:56 +00:00
'addr' => $this -> session -> get ( 'addr' ),
2019-12-03 21:29:37 +00:00
'remote_addr' => $_SERVER [ 'REMOTE_ADDR' ]]
);
2019-12-30 02:52:56 +00:00
$this -> session -> clear ();
2019-12-03 21:29:37 +00:00
$this -> baseUrl -> redirect ();
}
$user = $this -> dba -> selectFirst (
'user' ,
[],
[
2019-12-09 23:44:56 +00:00
'uid' => $this -> session -> get ( 'uid' ),
2019-12-03 21:29:37 +00:00
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
if ( ! $this -> dba -> isResult ( $user )) {
2019-12-30 02:52:56 +00:00
$this -> session -> clear ();
2019-12-03 21:29:37 +00:00
$this -> baseUrl -> redirect ();
}
// Make sure to refresh the last login time for the user if the user
// stays logged in for a long time, e.g. with "Remember Me"
$login_refresh = false ;
2019-12-09 23:44:56 +00:00
if ( ! $this -> session -> get ( 'last_login_date' )) {
$this -> session -> set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-12-03 21:29:37 +00:00
}
2019-12-09 23:44:56 +00:00
if ( strcmp ( DateTimeFormat :: utc ( 'now - 12 hours' ), $this -> session -> get ( 'last_login_date' )) > 0 ) {
$this -> session -> set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-12-03 21:29:37 +00:00
$login_refresh = true ;
}
$this -> setForUser ( $a , $user , false , false , $login_refresh );
}
}
}
2019-12-03 20:18:26 +00:00
/**
* Attempts to authenticate using OpenId
*
* @ param string $openid_url OpenID URL string
* @ param bool $remember Whether to set the session remember flag
2019-12-03 21:29:37 +00:00
*
* @ throws HttpException\InternalServerErrorException In case of Friendica internal exceptions
2019-12-03 20:18:26 +00:00
*/
2019-12-03 21:29:37 +00:00
public function withOpenId ( string $openid_url , bool $remember )
2019-12-03 20:18:26 +00:00
{
2019-12-03 21:29:37 +00:00
$noid = $this -> config -> get ( 'system' , 'no_openid' );
2019-12-03 20:18:26 +00:00
// if it's an email address or doesn't resolve to a URL, fail.
if ( $noid || strpos ( $openid_url , '@' ) || ! Network :: isUrlValid ( $openid_url )) {
2020-07-23 06:25:01 +00:00
notice ( $this -> l10n -> t ( 'Login failed.' ));
2019-12-03 21:29:37 +00:00
$this -> baseUrl -> redirect ();
2019-12-03 20:18:26 +00:00
}
// Otherwise it's probably an openid.
try {
2019-12-03 21:29:37 +00:00
$openid = new LightOpenID ( $this -> baseUrl -> getHostname ());
2019-12-03 20:18:26 +00:00
$openid -> identity = $openid_url ;
2019-12-09 23:44:56 +00:00
$this -> session -> set ( 'openid' , $openid_url );
$this -> session -> set ( 'remember' , $remember );
2019-12-03 21:29:37 +00:00
$openid -> returnUrl = $this -> baseUrl -> get ( true ) . '/openid' ;
$openid -> optional = [ 'namePerson/friendly' , 'contact/email' , 'namePerson' , 'namePerson/first' , 'media/image/aspect11' , 'media/image/default' ];
2019-12-03 20:18:26 +00:00
System :: externalRedirect ( $openid -> authUrl ());
} catch ( Exception $e ) {
2019-12-03 21:29:37 +00:00
notice ( $this -> l10n -> t ( 'We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.' ) . '<br /><br >' . $this -> l10n -> t ( 'The error message was:' ) . ' ' . $e -> getMessage ());
2019-12-03 20:18:26 +00:00
}
}
/**
* Attempts to authenticate using login / password
*
2019-12-03 21:29:37 +00:00
* @ param App $a The Friendica Application context
* @ param string $username User name
* @ param string $password Clear password
* @ param bool $remember Whether to set the session remember flag
*
* @ throws HttpException\InternalServerErrorException In case of Friendica internal exceptions
* @ throws Exception A general Exception ( like SQL Grammar exceptions )
2019-12-03 20:18:26 +00:00
*/
2019-12-03 21:29:37 +00:00
public function withPassword ( App $a , string $username , string $password , bool $remember )
2019-12-03 20:18:26 +00:00
{
$record = null ;
try {
2021-05-19 19:57:58 +00:00
$record = $this -> dba -> selectFirst (
'user' ,
[],
[ 'uid' => User :: getIdFromPasswordAuthentication ( $username , $password )]
);
2019-12-03 20:18:26 +00:00
} catch ( Exception $e ) {
2019-12-03 21:29:37 +00:00
$this -> logger -> warning ( 'authenticate: failed login attempt' , [ 'action' => 'login' , 'username' => Strings :: escapeTags ( $username ), 'ip' => $_SERVER [ 'REMOTE_ADDR' ]]);
2020-07-23 06:25:01 +00:00
notice ( $this -> l10n -> t ( 'Login failed. Please check your credentials.' ));
2019-12-03 21:29:37 +00:00
$this -> baseUrl -> redirect ();
2019-12-03 20:18:26 +00:00
}
if ( ! $remember ) {
2019-12-08 21:45:34 +00:00
$this -> cookie -> clear ();
2019-12-03 20:18:26 +00:00
}
// if we haven't failed up this point, log them in.
2019-12-09 23:44:56 +00:00
$this -> session -> set ( 'remember' , $remember );
$this -> session -> set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-12-03 20:18:26 +00:00
2019-12-09 23:44:56 +00:00
$openid_identity = $this -> session -> get ( 'openid_identity' );
$openid_server = $this -> session -> get ( 'openid_server' );
2019-12-03 21:29:37 +00:00
2019-12-03 20:18:26 +00:00
if ( ! empty ( $openid_identity ) || ! empty ( $openid_server )) {
2019-12-03 21:29:37 +00:00
$this -> dba -> update ( 'user' , [ 'openid' => $openid_identity , 'openidserver' => $openid_server ], [ 'uid' => $record [ 'uid' ]]);
2019-12-03 20:18:26 +00:00
}
2019-12-03 21:29:37 +00:00
$this -> setForUser ( $a , $record , true , true );
2019-12-03 20:18:26 +00:00
2019-12-09 23:44:56 +00:00
$return_path = $this -> session -> get ( 'return_path' , '' );
$this -> session -> remove ( 'return_path' );
2019-12-03 20:18:26 +00:00
2019-12-03 21:29:37 +00:00
$this -> baseUrl -> redirect ( $return_path );
2019-12-03 20:18:26 +00:00
}
/**
2020-01-19 06:05:23 +00:00
* Sets the provided user ' s authenticated session
2019-12-03 21:29:37 +00:00
*
* @ param App $a The Friendica application context
* @ param array $user_record The current " user " record
* @ param bool $login_initial
* @ param bool $interactive
* @ param bool $login_refresh
2019-12-03 20:18:26 +00:00
*
2019-12-03 21:29:37 +00:00
* @ throws HTTPException\InternalServerErrorException In case of Friendica specific exceptions
* @ throws Exception In case of general Exceptions ( like SQL Grammar exceptions )
2019-12-03 20:18:26 +00:00
*/
2019-12-03 21:29:37 +00:00
public function setForUser ( App $a , array $user_record , bool $login_initial = false , bool $interactive = false , bool $login_refresh = false )
2019-12-03 20:18:26 +00:00
{
2019-12-09 23:44:56 +00:00
$this -> session -> setMultiple ([
2019-12-03 21:29:37 +00:00
'uid' => $user_record [ 'uid' ],
'theme' => $user_record [ 'theme' ],
2020-01-18 15:50:57 +00:00
'mobile-theme' => $this -> pConfig -> get ( $user_record [ 'uid' ], 'system' , 'mobile_theme' ),
2019-12-03 21:29:37 +00:00
'authenticated' => 1 ,
'page_flags' => $user_record [ 'page-flags' ],
'my_url' => $this -> baseUrl -> get () . '/profile/' . $user_record [ 'nickname' ],
'my_address' => $user_record [ 'nickname' ] . '@' . substr ( $this -> baseUrl -> get (), strpos ( $this -> baseUrl -> get (), '://' ) + 3 ),
'addr' => ( $_SERVER [ 'REMOTE_ADDR' ] ? ? '' ) ? : '0.0.0.0'
]);
Session :: setVisitorsContacts ();
$member_since = strtotime ( $user_record [ 'register_date' ]);
2019-12-09 23:44:56 +00:00
$this -> session -> set ( 'new_member' , time () < ( $member_since + ( 60 * 60 * 24 * 14 )));
2019-12-03 21:29:37 +00:00
if ( strlen ( $user_record [ 'timezone' ])) {
date_default_timezone_set ( $user_record [ 'timezone' ]);
2021-07-24 21:16:53 +00:00
$a -> setTimeZone ( $user_record [ 'timezone' ]);
2019-12-03 21:29:37 +00:00
}
2019-12-03 20:18:26 +00:00
2021-07-24 11:49:11 +00:00
$contact = $this -> dba -> selectFirst ( 'contact' , [ 'id' ], [ 'uid' => $user_record [ 'uid' ], 'self' => true ]);
2019-12-03 21:29:37 +00:00
if ( $this -> dba -> isResult ( $contact )) {
2021-07-24 20:34:07 +00:00
$a -> setContactId ( $contact [ 'id' ]);
2021-07-24 10:09:39 +00:00
$this -> session -> set ( 'cid' , $contact [ 'id' ]);
2019-12-03 21:29:37 +00:00
}
2019-12-03 20:18:26 +00:00
2019-12-03 21:29:37 +00:00
header ( 'X-Account-Management-Status: active; name="' . $user_record [ 'username' ] . '"; id="' . $user_record [ 'nickname' ] . '"' );
2019-12-03 20:18:26 +00:00
2019-12-03 21:29:37 +00:00
if ( $login_initial || $login_refresh ) {
$this -> dba -> update ( 'user' , [ 'login_date' => DateTimeFormat :: utcNow ()], [ 'uid' => $user_record [ 'uid' ]]);
2019-12-03 20:18:26 +00:00
2019-12-03 21:29:37 +00:00
// Set the login date for all identities of the user
2021-07-24 13:29:58 +00:00
$this -> dba -> update ( 'user' , [ 'login_date' => DateTimeFormat :: utcNow ()],
[ 'parent-uid' => $user_record [ 'uid' ], 'account_removed' => false ]);
2019-12-03 21:29:37 +00:00
}
2019-12-03 20:18:26 +00:00
2019-12-03 21:29:37 +00:00
if ( $login_initial ) {
/*
* If the user specified to remember the authentication , then set a cookie
* that expires after one week ( the default is when the browser is closed ) .
* The cookie will be renewed automatically .
* The week ensures that sessions will expire after some inactivity .
2020-09-30 14:53:18 +00:00
*/
2019-12-09 23:44:56 +00:00
if ( $this -> session -> get ( 'remember' )) {
2019-12-15 22:46:56 +00:00
$this -> logger -> info ( 'Injecting cookie for remembered user ' . $user_record [ 'nickname' ]);
2021-01-17 22:30:18 +00:00
$this -> cookie -> setMultiple ([
'uid' => $user_record [ 'uid' ],
'hash' => $this -> cookie -> hashPrivateData ( $user_record [ 'password' ], $user_record [ 'prvkey' ]),
]);
2019-12-09 23:44:56 +00:00
$this -> session -> remove ( 'remember' );
2019-12-03 20:18:26 +00:00
}
}
2021-07-25 15:23:37 +00:00
$this -> redirectForTwoFactorAuthentication ( $user_record [ 'uid' ]);
2018-10-17 12:19:58 +00:00
2019-12-03 21:29:37 +00:00
if ( $interactive ) {
if ( $user_record [ 'login_date' ] <= DBA :: NULL_DATETIME ) {
info ( $this -> l10n -> t ( 'Welcome %s' , $user_record [ 'username' ]));
info ( $this -> l10n -> t ( 'Please upload a profile photo.' ));
2019-10-27 13:56:27 +00:00
$this -> baseUrl -> redirect ( 'settings/profile/photo/new' );
2019-12-03 21:29:37 +00:00
}
2018-10-17 12:19:58 +00:00
}
2021-08-08 10:14:56 +00:00
$a -> setUserId ( $user_record [ 'uid' ]);
$a -> setNickname ( $user_record [ 'nickname' ]);
2019-12-03 21:29:37 +00:00
if ( $login_initial ) {
2021-08-08 10:14:56 +00:00
Hook :: callAll ( 'logged_in' , $user_record );
2018-10-17 12:19:58 +00:00
2019-12-16 00:35:26 +00:00
if ( DI :: module () -> getName () !== 'home' && $this -> session -> exists ( 'return_path' )) {
2019-12-09 23:44:56 +00:00
$this -> baseUrl -> redirect ( $this -> session -> get ( 'return_path' ));
2019-12-03 21:29:37 +00:00
}
}
2018-10-17 12:19:58 +00:00
}
/**
2021-01-17 22:30:18 +00:00
* Decides whether to redirect the user to two - factor authentication .
* All return calls in this method skip two - factor authentication
*
2019-12-03 21:29:37 +00:00
* @ param int $uid The User Identified
*
* @ throws HTTPException\ForbiddenException In case the two factor authentication is forbidden ( e . g . for AJAX calls )
2021-01-17 22:30:18 +00:00
* @ throws HTTPException\InternalServerErrorException
2018-10-17 12:19:58 +00:00
*/
2021-07-25 15:23:37 +00:00
private function redirectForTwoFactorAuthentication ( int $uid )
2019-05-13 05:36:09 +00:00
{
// Check user setting, if 2FA disabled return
2020-01-18 15:50:57 +00:00
if ( ! $this -> pConfig -> get ( $uid , '2fa' , 'verified' )) {
2019-05-13 05:36:09 +00:00
return ;
}
2021-01-17 22:30:18 +00:00
// Check current path, if public or 2fa module return
2021-07-25 15:23:37 +00:00
if ( DI :: args () -> getArgc () > 0 && in_array ( DI :: args () -> getArgv ()[ 0 ], [ '2fa' , 'view' , 'help' , 'api' , 'proxy' , 'logout' ])) {
2019-05-13 05:36:09 +00:00
return ;
}
2021-01-19 04:32:48 +00:00
// Case 1a: 2FA session already present: return
2019-12-09 23:44:56 +00:00
if ( $this -> session -> get ( '2fa' )) {
2019-05-13 05:36:09 +00:00
return ;
}
2021-01-19 04:32:48 +00:00
// Case 1b: Check for trusted browser
if ( $this -> cookie -> get ( 'trusted' )) {
// Retrieve a trusted_browser model based on cookie hash
$trustedBrowserRepository = new TrustedBrowser ( $this -> dba , $this -> logger );
try {
$trustedBrowser = $trustedBrowserRepository -> selectOneByHash ( $this -> cookie -> get ( 'trusted' ));
// Verify record ownership
if ( $trustedBrowser -> uid === $uid ) {
// Update last_used date
$trustedBrowser -> recordUse ();
// Save it to the database
$trustedBrowserRepository -> save ( $trustedBrowser );
// Set 2fa session key and return
$this -> session -> set ( '2fa' , true );
return ;
} else {
// Invalid trusted cookie value, removing it
$this -> cookie -> unset ( 'trusted' );
}
} catch ( \Throwable $e ) {
// Local trusted browser record was probably removed by the user, we carry on with 2FA
}
}
2019-05-13 05:36:09 +00:00
// Case 2: No valid 2FA session: redirect to code verification page
2019-12-15 23:30:39 +00:00
if ( $this -> mode -> isAjax ()) {
2019-12-03 21:29:37 +00:00
throw new HTTPException\ForbiddenException ();
2019-07-24 00:03:08 +00:00
} else {
2019-12-15 23:28:31 +00:00
$this -> baseUrl -> redirect ( '2fa' );
2019-07-24 00:03:08 +00:00
}
2019-05-13 05:36:09 +00:00
}
2018-10-17 12:19:58 +00:00
}