2018-10-17 14:19:58 +02:00
< ? php
/**
2022-01-02 08:27:47 +01:00
* @ copyright Copyright ( C ) 2010 - 2022 , the Friendica project
2020-02-08 17:16:42 +01: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 14:19:58 +02:00
*/
2020-09-30 16:53:18 +02:00
namespace Friendica\Security ;
2018-10-17 14:19:58 +02:00
2019-12-03 22:29:37 +01:00
use Exception ;
2019-05-13 07:36:09 +02:00
use Friendica\App ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Config\Capability\IManageConfigValues ;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues ;
2019-12-08 22:45:34 +01:00
use Friendica\Core\Hook ;
2022-10-23 20:41:17 +02:00
use Friendica\Core\Session\Capability\IHandleUserSessions ;
2019-12-08 22:45:34 +01:00
use Friendica\Core\System ;
2019-12-03 22:29:37 +01:00
use Friendica\Database\Database ;
2019-12-03 21:18:26 +01:00
use Friendica\Database\DBA ;
2019-12-16 01:35:26 +01:00
use Friendica\DI ;
2019-12-03 21:18:26 +01:00
use Friendica\Model\User ;
2019-12-03 22:29:37 +01:00
use Friendica\Network\HTTPException ;
2021-01-19 05:32:48 +01:00
use Friendica\Security\TwoFactor\Repository\TrustedBrowser ;
2019-12-03 21:18:26 +01:00
use Friendica\Util\DateTimeFormat ;
use Friendica\Util\Network ;
2019-12-03 22:29:37 +01:00
use LightOpenID ;
2020-01-18 20:59:39 +01:00
use Friendica\Core\L10n ;
2022-11-30 06:59:27 +01:00
use Friendica\Core\Worker ;
2022-11-30 23:34:50 +01:00
use Friendica\Model\Contact ;
2019-12-03 22:29:37 +01:00
use Psr\Log\LoggerInterface ;
2018-10-17 14:19:58 +02:00
/**
2020-09-30 16:53:18 +02:00
* Handle Authentication , Session and Cookies
2019-10-11 01:21:41 +02:00
*/
2019-12-03 22:29:37 +01:00
class Authentication
2018-10-17 14:19:58 +02:00
{
2021-10-26 21:44:29 +02:00
/** @var IManageConfigValues */
2019-12-03 22:29:37 +01:00
private $config ;
2019-12-16 00:30:39 +01:00
/** @var App\Mode */
private $mode ;
2019-12-03 22:29:37 +01:00
/** @var App\BaseURL */
private $baseUrl ;
/** @var L10n */
private $l10n ;
/** @var Database */
private $dba ;
/** @var LoggerInterface */
private $logger ;
2019-12-08 22:45:34 +01:00
/** @var User\Cookie */
private $cookie ;
2022-10-23 20:41:17 +02:00
/** @var IHandleUserSessions */
2019-12-10 00:44:56 +01:00
private $session ;
2021-10-26 21:44:29 +02:00
/** @var IManagePersonalConfigValues */
2020-01-18 16:50:57 +01:00
private $pConfig ;
2022-06-23 22:42:35 +02:00
/** @var string */
private $remoteAddress ;
2019-12-03 22:29:37 +01:00
2021-11-12 21:08:36 +01:00
/**
* Sets the X - Account - Management - Status header
*
* mainly extracted to make it overridable for tests
*
* @ param array $user_record
*/
protected function setXAccMgmtStatusHeader ( array $user_record )
{
header ( 'X-Account-Management-Status: active; name="' . $user_record [ 'username' ] . '"; id="' . $user_record [ 'nickname' ] . '"' );
}
2019-12-03 22:29:37 +01:00
/**
* Authentication constructor .
*
2022-06-23 22:42:35 +02:00
* @ param IManageConfigValues $config
* @ param App\Mode $mode
* @ param App\BaseURL $baseUrl
* @ param L10n $l10n
* @ param Database $dba
* @ param LoggerInterface $logger
* @ param User\Cookie $cookie
2022-10-23 20:41:17 +02:00
* @ param IHandleUserSessions $session
2022-06-23 22:42:35 +02:00
* @ param IManagePersonalConfigValues $pConfig
* @ param App\Request $request
2019-12-03 22:29:37 +01:00
*/
2022-10-23 20:41:17 +02:00
public function __construct ( IManageConfigValues $config , App\Mode $mode , App\BaseURL $baseUrl , L10n $l10n , Database $dba , LoggerInterface $logger , User\Cookie $cookie , IHandleUserSessions $session , IManagePersonalConfigValues $pConfig , App\Request $request )
2019-12-03 22:29:37 +01:00
{
2022-06-23 22:42:35 +02:00
$this -> config = $config ;
$this -> mode = $mode ;
$this -> baseUrl = $baseUrl ;
$this -> l10n = $l10n ;
$this -> dba = $dba ;
$this -> logger = $logger ;
$this -> cookie = $cookie ;
$this -> session = $session ;
$this -> pConfig = $pConfig ;
$this -> remoteAddress = $request -> getRemoteAddress ();
2019-12-03 22:29:37 +01:00
}
/**
2020-01-19 07:05:23 +01:00
* Tries to auth the user from the cookie or session
2019-12-03 22:29:37 +01: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 22:45:34 +01:00
public function withSession ( App $a )
2019-12-03 22:29:37 +01:00
{
2019-12-08 22:45:34 +01:00
// When the "Friendica" cookie is set, take the value to authenticate and renew the cookie.
2021-01-17 23:30:18 +01:00
if ( $this -> cookie -> get ( 'uid' )) {
2019-12-08 22:45:34 +01:00
$user = $this -> dba -> selectFirst (
'user' ,
[],
[
2021-01-17 23:30:18 +01:00
'uid' => $this -> cookie -> get ( 'uid' ),
2019-12-08 22:45:34 +01:00
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2019-12-10 00:44:56 +01:00
if ( $this -> dba -> isResult ( $user )) {
2021-01-17 23:30:18 +01:00
if ( ! $this -> cookie -> comparePrivateDataHash ( $this -> cookie -> get ( 'hash' ),
2019-12-08 22:45:34 +01:00
$user [ 'password' ] ? ? '' ,
2021-01-17 23:30:18 +01:00
$user [ 'prvkey' ] ? ? '' )
) {
$this -> logger -> notice ( " Hash doesn't fit. " , [ 'user' => $this -> cookie -> get ( 'uid' )]);
2019-12-30 03:52:56 +01:00
$this -> session -> clear ();
2020-01-09 01:51:54 +01:00
$this -> cookie -> clear ();
2019-12-08 22:45:34 +01:00
$this -> baseUrl -> redirect ();
}
2019-12-03 22:29:37 +01:00
2019-12-08 22:45:34 +01:00
// Renew the cookie
2021-01-17 23:30:18 +01:00
$this -> cookie -> send ();
2019-12-03 22:29:37 +01:00
2022-06-25 14:45:33 +02:00
// Do the authentication if not done by now
2019-12-10 00:44:56 +01:00
if ( ! $this -> session -> get ( 'authenticated' )) {
2019-12-08 22:45:34 +01:00
$this -> setForUser ( $a , $user );
2019-12-03 22:29:37 +01:00
2019-12-08 22:45:34 +01:00
if ( $this -> config -> get ( 'system' , 'paranoia' )) {
2021-01-17 23:30:18 +01:00
$this -> session -> set ( 'addr' , $this -> cookie -> get ( 'ip' ));
2019-12-03 22:29:37 +01:00
}
}
}
}
2019-12-10 00:44:56 +01:00
if ( $this -> session -> get ( 'authenticated' )) {
if ( $this -> session -> get ( 'visitor_id' ) && ! $this -> session -> get ( 'uid' )) {
2021-07-24 13:49:11 +02:00
$contact = $this -> dba -> selectFirst ( 'contact' , [ 'id' ], [ 'id' => $this -> session -> get ( 'visitor_id' )]);
2019-12-03 22:29:37 +01:00
if ( $this -> dba -> isResult ( $contact )) {
2021-07-24 22:34:07 +02:00
$a -> setContactId ( $contact [ 'id' ]);
2019-12-03 22:29:37 +01:00
}
}
2019-12-10 00:44:56 +01:00
if ( $this -> session -> get ( 'uid' )) {
2019-12-03 22:29:37 +01:00
// already logged in user returning
$check = $this -> config -> get ( 'system' , 'paranoia' );
// extra paranoia - if the IP changed, log them out
2022-06-23 22:42:35 +02:00
if ( $check && ( $this -> session -> get ( 'addr' ) != $this -> remoteAddress )) {
2019-12-03 22:29:37 +01:00
$this -> logger -> notice ( 'Session address changed. Paranoid setting in effect, blocking session. ' , [
2022-06-23 22:42:35 +02:00
'addr' => $this -> session -> get ( 'addr' ),
'remote_addr' => $this -> remoteAddress
]
2019-12-03 22:29:37 +01:00
);
2019-12-30 03:52:56 +01:00
$this -> session -> clear ();
2019-12-03 22:29:37 +01:00
$this -> baseUrl -> redirect ();
}
$user = $this -> dba -> selectFirst (
'user' ,
[],
[
2019-12-10 00:44:56 +01:00
'uid' => $this -> session -> get ( 'uid' ),
2019-12-03 22:29:37 +01:00
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
if ( ! $this -> dba -> isResult ( $user )) {
2019-12-30 03:52:56 +01:00
$this -> session -> clear ();
2019-12-03 22:29:37 +01: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-10 00:44:56 +01:00
if ( ! $this -> session -> get ( 'last_login_date' )) {
$this -> session -> set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-12-03 22:29:37 +01:00
}
2019-12-10 00:44:56 +01: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 22:29:37 +01:00
$login_refresh = true ;
}
$this -> setForUser ( $a , $user , false , false , $login_refresh );
}
}
}
2019-12-03 21:18:26 +01: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 22:29:37 +01:00
*
* @ throws HttpException\InternalServerErrorException In case of Friendica internal exceptions
2019-12-03 21:18:26 +01:00
*/
2019-12-03 22:29:37 +01:00
public function withOpenId ( string $openid_url , bool $remember )
2019-12-03 21:18:26 +01:00
{
2019-12-03 22:29:37 +01:00
$noid = $this -> config -> get ( 'system' , 'no_openid' );
2019-12-03 21:18:26 +01:00
// if it's an email address or doesn't resolve to a URL, fail.
if ( $noid || strpos ( $openid_url , '@' ) || ! Network :: isUrlValid ( $openid_url )) {
2022-10-17 20:55:22 +02:00
DI :: sysmsg () -> addNotice ( $this -> l10n -> t ( 'Login failed.' ));
2019-12-03 22:29:37 +01:00
$this -> baseUrl -> redirect ();
2019-12-03 21:18:26 +01:00
}
// Otherwise it's probably an openid.
try {
2019-12-03 22:29:37 +01:00
$openid = new LightOpenID ( $this -> baseUrl -> getHostname ());
2019-12-03 21:18:26 +01:00
$openid -> identity = $openid_url ;
2019-12-10 00:44:56 +01:00
$this -> session -> set ( 'openid' , $openid_url );
$this -> session -> set ( 'remember' , $remember );
2019-12-03 22:29:37 +01: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 21:18:26 +01:00
System :: externalRedirect ( $openid -> authUrl ());
} catch ( Exception $e ) {
2022-10-17 20:55:22 +02:00
DI :: sysmsg () -> addNotice ( $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 21:18:26 +01:00
}
}
/**
* Attempts to authenticate using login / password
*
2022-08-01 17:38:54 +02:00
* @ param App $a The Friendica Application context
* @ param string $username
* @ param string $password Clear password
* @ param bool $remember Whether to set the session remember flag
* @ param string $return_path The relative path to redirect the user to after authentication
2019-12-03 22:29:37 +01:00
*
2022-08-01 17:38:54 +02:00
* @ throws HTTPException\ForbiddenException
* @ throws HTTPException\FoundException
* @ throws HTTPException\InternalServerErrorException In case of Friendica internal exceptions
* @ throws HTTPException\MovedPermanentlyException
* @ throws HTTPException\TemporaryRedirectException
2019-12-03 21:18:26 +01:00
*/
2022-08-01 17:38:54 +02:00
public function withPassword ( App $a , string $username , string $password , bool $remember , string $return_path = '' )
2019-12-03 21:18:26 +01:00
{
$record = null ;
try {
2021-05-19 21:57:58 +02:00
$record = $this -> dba -> selectFirst (
'user' ,
[],
[ 'uid' => User :: getIdFromPasswordAuthentication ( $username , $password )]
);
2019-12-03 21:18:26 +01:00
} catch ( Exception $e ) {
2022-06-23 22:42:35 +02:00
$this -> logger -> warning ( 'authenticate: failed login attempt' , [ 'action' => 'login' , 'username' => $username , 'ip' => $this -> remoteAddress ]);
2022-10-17 20:55:22 +02:00
DI :: sysmsg () -> addNotice ( $this -> l10n -> t ( 'Login failed. Please check your credentials.' ));
2019-12-03 22:29:37 +01:00
$this -> baseUrl -> redirect ();
2019-12-03 21:18:26 +01:00
}
if ( ! $remember ) {
2022-06-25 14:45:33 +02:00
$trusted = $this -> cookie -> get ( '2fa_cookie_hash' ) ? ? null ;
2019-12-08 22:45:34 +01:00
$this -> cookie -> clear ();
2022-06-25 14:45:33 +02:00
if ( $trusted ) {
$this -> cookie -> set ( '2fa_cookie_hash' , $trusted );
}
2019-12-03 21:18:26 +01:00
}
// if we haven't failed up this point, log them in.
2019-12-10 00:44:56 +01:00
$this -> session -> set ( 'remember' , $remember );
$this -> session -> set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-12-03 21:18:26 +01:00
2019-12-10 00:44:56 +01:00
$openid_identity = $this -> session -> get ( 'openid_identity' );
$openid_server = $this -> session -> get ( 'openid_server' );
2019-12-03 22:29:37 +01:00
2019-12-03 21:18:26 +01:00
if ( ! empty ( $openid_identity ) || ! empty ( $openid_server )) {
2019-12-03 22:29:37 +01:00
$this -> dba -> update ( 'user' , [ 'openid' => $openid_identity , 'openidserver' => $openid_server ], [ 'uid' => $record [ 'uid' ]]);
2019-12-03 21:18:26 +01:00
}
2022-08-01 17:42:10 +02:00
/**
* @ see User :: getPasswordRegExp ()
*/
if ( PASSWORD_DEFAULT === PASSWORD_BCRYPT && strlen ( $password ) > 72 ) {
$return_path = '/security/password_too_long?' . http_build_query ([ 'return_path' => $return_path ]);
}
2019-12-03 21:18:26 +01:00
2022-08-01 17:42:10 +02:00
$this -> setForUser ( $a , $record , true , true );
2019-12-03 21:18:26 +01:00
2019-12-03 22:29:37 +01:00
$this -> baseUrl -> redirect ( $return_path );
2019-12-03 21:18:26 +01:00
}
/**
2020-01-19 07:05:23 +01:00
* Sets the provided user ' s authenticated session
2019-12-03 22:29:37 +01: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 21:18:26 +01:00
*
2022-07-07 21:47:39 +02:00
* @ throws HTTPException\FoundException
* @ throws HTTPException\MovedPermanentlyException
* @ throws HTTPException\TemporaryRedirectException
* @ throws HTTPException\ForbiddenException
2019-12-03 22:29:37 +01:00
* @ throws HTTPException\InternalServerErrorException In case of Friendica specific exceptions
2022-07-07 21:47:39 +02:00
*
2019-12-03 21:18:26 +01:00
*/
2019-12-03 22:29:37 +01:00
public function setForUser ( App $a , array $user_record , bool $login_initial = false , bool $interactive = false , bool $login_refresh = false )
2019-12-03 21:18:26 +01:00
{
2019-12-10 00:44:56 +01:00
$this -> session -> setMultiple ([
2019-12-03 22:29:37 +01:00
'uid' => $user_record [ 'uid' ],
'theme' => $user_record [ 'theme' ],
2020-01-18 16:50:57 +01:00
'mobile-theme' => $this -> pConfig -> get ( $user_record [ 'uid' ], 'system' , 'mobile_theme' ),
2019-12-03 22:29:37 +01: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 ),
2022-06-23 22:42:35 +02:00
'addr' => $this -> remoteAddress ,
2022-10-23 20:41:17 +02:00
'nickname' => $user_record [ 'nickname' ],
2019-12-03 22:29:37 +01:00
]);
2022-10-23 20:41:17 +02:00
$this -> session -> setVisitorsContacts ();
2019-12-03 22:29:37 +01:00
$member_since = strtotime ( $user_record [ 'register_date' ]);
2019-12-10 00:44:56 +01:00
$this -> session -> set ( 'new_member' , time () < ( $member_since + ( 60 * 60 * 24 * 14 )));
2019-12-03 22:29:37 +01:00
if ( strlen ( $user_record [ 'timezone' ])) {
2021-07-24 23:16:53 +02:00
$a -> setTimeZone ( $user_record [ 'timezone' ]);
2019-12-03 22:29:37 +01:00
}
2019-12-03 21:18:26 +01:00
2021-07-24 13:49:11 +02:00
$contact = $this -> dba -> selectFirst ( 'contact' , [ 'id' ], [ 'uid' => $user_record [ 'uid' ], 'self' => true ]);
2019-12-03 22:29:37 +01:00
if ( $this -> dba -> isResult ( $contact )) {
2021-07-24 22:34:07 +02:00
$a -> setContactId ( $contact [ 'id' ]);
2021-07-24 12:09:39 +02:00
$this -> session -> set ( 'cid' , $contact [ 'id' ]);
2019-12-03 22:29:37 +01:00
}
2019-12-03 21:18:26 +01:00
2021-11-12 21:08:36 +01:00
$this -> setXAccMgmtStatusHeader ( $user_record );
2019-12-03 21:18:26 +01:00
2019-12-03 22:29:37 +01:00
if ( $login_initial || $login_refresh ) {
2022-12-01 07:02:51 +01:00
$this -> dba -> update ( 'user' , [ 'last-activity' => DateTimeFormat :: utcNow ( 'Y-m-d' ), 'login_date' => DateTimeFormat :: utcNow ()], [ 'uid' => $user_record [ 'uid' ]]);
2019-12-03 21:18:26 +01:00
2019-12-03 22:29:37 +01:00
// Set the login date for all identities of the user
2022-12-04 08:03:11 +01:00
$this -> dba -> update ( 'user' , [ 'last-activity' => DateTimeFormat :: utcNow ( 'Y-m-d' ), 'login_date' => DateTimeFormat :: utcNow ()],
2021-07-24 15:29:58 +02:00
[ 'parent-uid' => $user_record [ 'uid' ], 'account_removed' => false ]);
2022-11-30 06:59:27 +01:00
2022-11-30 23:34:50 +01:00
// Regularly update suggestions
2022-12-01 06:53:18 +01:00
if ( Contact\Relation :: areSuggestionsOutdated ( $user_record [ 'uid' ])) {
Worker :: add ( Worker :: PRIORITY_MEDIUM , 'UpdateSuggestions' , $user_record [ 'uid' ]);
}
2019-12-03 22:29:37 +01:00
}
2019-12-03 21:18:26 +01:00
2019-12-03 22:29:37 +01: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 16:53:18 +02:00
*/
2019-12-10 00:44:56 +01:00
if ( $this -> session -> get ( 'remember' )) {
2019-12-15 23:46:56 +01:00
$this -> logger -> info ( 'Injecting cookie for remembered user ' . $user_record [ 'nickname' ]);
2021-01-17 23:30:18 +01:00
$this -> cookie -> setMultiple ([
'uid' => $user_record [ 'uid' ],
'hash' => $this -> cookie -> hashPrivateData ( $user_record [ 'password' ], $user_record [ 'prvkey' ]),
]);
2019-12-10 00:44:56 +01:00
$this -> session -> remove ( 'remember' );
2019-12-03 21:18:26 +01:00
}
}
2021-07-25 17:23:37 +02:00
$this -> redirectForTwoFactorAuthentication ( $user_record [ 'uid' ]);
2018-10-17 14:19:58 +02:00
2019-12-03 22:29:37 +01:00
if ( $interactive ) {
if ( $user_record [ 'login_date' ] <= DBA :: NULL_DATETIME ) {
2022-10-17 20:55:22 +02:00
DI :: sysmsg () -> addInfo ( $this -> l10n -> t ( 'Welcome %s' , $user_record [ 'username' ]));
DI :: sysmsg () -> addInfo ( $this -> l10n -> t ( 'Please upload a profile photo.' ));
2019-10-27 14:56:27 +01:00
$this -> baseUrl -> redirect ( 'settings/profile/photo/new' );
2019-12-03 22:29:37 +01:00
}
2018-10-17 14:19:58 +02:00
}
2021-08-09 22:56:15 +02:00
$a -> setLoggedInUserId ( $user_record [ 'uid' ]);
$a -> setLoggedInUserNickname ( $user_record [ 'nickname' ]);
2019-12-03 22:29:37 +01:00
if ( $login_initial ) {
2021-08-08 12:14:56 +02:00
Hook :: callAll ( 'logged_in' , $user_record );
2019-12-03 22:29:37 +01:00
}
2018-10-17 14:19:58 +02:00
}
/**
2021-01-17 23:30:18 +01:00
* Decides whether to redirect the user to two - factor authentication .
* All return calls in this method skip two - factor authentication
*
2019-12-03 22:29:37 +01: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 23:30:18 +01:00
* @ throws HTTPException\InternalServerErrorException
2018-10-17 14:19:58 +02:00
*/
2021-07-25 17:23:37 +02:00
private function redirectForTwoFactorAuthentication ( int $uid )
2019-05-13 07:36:09 +02:00
{
// Check user setting, if 2FA disabled return
2020-01-18 16:50:57 +01:00
if ( ! $this -> pConfig -> get ( $uid , '2fa' , 'verified' )) {
2019-05-13 07:36:09 +02:00
return ;
}
2021-01-17 23:30:18 +01:00
// Check current path, if public or 2fa module return
2021-07-25 17:23:37 +02:00
if ( DI :: args () -> getArgc () > 0 && in_array ( DI :: args () -> getArgv ()[ 0 ], [ '2fa' , 'view' , 'help' , 'api' , 'proxy' , 'logout' ])) {
2019-05-13 07:36:09 +02:00
return ;
}
2021-01-19 05:32:48 +01:00
// Case 1a: 2FA session already present: return
2019-12-10 00:44:56 +01:00
if ( $this -> session -> get ( '2fa' )) {
2019-05-13 07:36:09 +02:00
return ;
}
2021-01-19 05:32:48 +01:00
// Case 1b: Check for trusted browser
2022-06-25 14:45:33 +02:00
if ( $this -> cookie -> get ( '2fa_cookie_hash' )) {
2021-01-19 05:32:48 +01:00
// Retrieve a trusted_browser model based on cookie hash
$trustedBrowserRepository = new TrustedBrowser ( $this -> dba , $this -> logger );
try {
2022-06-25 14:45:33 +02:00
$trustedBrowser = $trustedBrowserRepository -> selectOneByHash ( $this -> cookie -> get ( '2fa_cookie_hash' ));
2021-01-19 05:32:48 +01:00
// Verify record ownership
if ( $trustedBrowser -> uid === $uid ) {
// Update last_used date
$trustedBrowser -> recordUse ();
// Save it to the database
$trustedBrowserRepository -> save ( $trustedBrowser );
2022-06-25 14:45:33 +02:00
// Only use this entry, if its really trusted, otherwise just update the record and proceed
if ( $trustedBrowser -> trusted ) {
// Set 2fa session key and return
$this -> session -> set ( '2fa' , true );
2021-01-19 05:32:48 +01:00
2022-06-25 14:45:33 +02:00
return ;
}
2021-01-19 05:32:48 +01:00
} 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 07:36:09 +02:00
// Case 2: No valid 2FA session: redirect to code verification page
2019-12-16 00:30:39 +01:00
if ( $this -> mode -> isAjax ()) {
2019-12-03 22:29:37 +01:00
throw new HTTPException\ForbiddenException ();
2019-07-24 02:03:08 +02:00
} else {
2019-12-16 00:28:31 +01:00
$this -> baseUrl -> redirect ( '2fa' );
2019-07-24 02:03:08 +02:00
}
2019-05-13 07:36:09 +02:00
}
2018-10-17 14:19:58 +02:00
}