2018-01-11 01:06:00 +01:00
< ? php
2019-10-11 01:21:41 +02:00
2018-01-21 19:33:59 +01:00
/**
* @ file src / Module / Login . php
*/
2019-10-11 01:21:41 +02:00
2018-01-11 01:06:00 +01:00
namespace Friendica\Module ;
2018-07-20 04:15:21 +02:00
use Exception ;
2018-01-11 01:06:00 +01:00
use Friendica\BaseModule ;
2018-10-17 14:19:58 +02:00
use Friendica\Core\Authentication ;
2018-01-11 01:06:00 +01:00
use Friendica\Core\Config ;
2018-12-26 07:06:24 +01:00
use Friendica\Core\Hook ;
2018-01-21 19:33:59 +01:00
use Friendica\Core\L10n ;
2018-10-29 22:20:46 +01:00
use Friendica\Core\Logger ;
2018-10-31 15:35:50 +01:00
use Friendica\Core\Renderer ;
2019-05-13 06:55:26 +02:00
use Friendica\Core\Session ;
2018-10-13 20:02:04 +02:00
use Friendica\Core\System ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2018-01-11 01:06:00 +01:00
use Friendica\Model\User ;
2018-01-27 03:38:34 +01:00
use Friendica\Util\DateTimeFormat ;
2018-01-27 05:51:41 +01:00
use Friendica\Util\Network ;
2018-11-08 16:14:37 +01:00
use Friendica\Util\Strings ;
2018-01-25 03:08:45 +01:00
use LightOpenID ;
2018-01-11 01:06:00 +01:00
/**
* Login module
*
2018-09-16 01:28:38 +02:00
* @ author Hypolite Petovan < hypolite @ mrpetovan . com >
2018-01-11 01:06:00 +01:00
*/
class Login extends BaseModule
{
public static function content ()
{
$a = self :: getApp ();
if ( local_user ()) {
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-01-11 01:06:00 +01:00
}
2019-05-26 22:15:38 +02:00
return self :: form ( Session :: get ( 'return_path' ), intval ( Config :: get ( 'config' , 'register_policy' )) !== \Friendica\Module\Register :: CLOSED );
2018-01-11 01:06:00 +01:00
}
public static function post ()
{
2019-05-26 22:15:38 +02:00
$return_path = Session :: get ( 'return_path' );
2018-01-11 01:06:00 +01:00
session_unset ();
2019-05-26 22:15:38 +02:00
Session :: set ( 'return_path' , $return_path );
2019-01-13 19:03:13 +01:00
2018-01-11 01:06:00 +01:00
// OpenId Login
if (
2018-02-09 06:08:01 +01:00
empty ( $_POST [ 'password' ])
2019-10-11 01:21:41 +02:00
&& ( ! empty ( $_POST [ 'openid_url' ])
|| ! empty ( $_POST [ 'username' ]))
2018-01-11 01:06:00 +01:00
) {
2018-02-09 06:08:01 +01:00
$openid_url = trim ( defaults ( $_POST , 'openid_url' , $_POST [ 'username' ]));
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +01:00
self :: openIdAuthentication ( $openid_url , ! empty ( $_POST [ 'remember' ]));
}
2018-01-11 01:06:00 +01:00
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_POST [ 'auth-params' ]) && $_POST [ 'auth-params' ] === 'login' ) {
2018-02-09 06:08:01 +01:00
self :: passwordAuthentication (
trim ( $_POST [ 'username' ]),
trim ( $_POST [ 'password' ]),
! empty ( $_POST [ 'remember' ])
);
}
}
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +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-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-02-09 06:08:01 +01:00
*/
private static function openIdAuthentication ( $openid_url , $remember )
{
$noid = Config :: get ( 'system' , 'no_openid' );
2018-10-13 20:02:04 +02:00
$a = self :: getApp ();
2018-02-09 06:08:01 +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 )) {
notice ( L10n :: t ( 'Login failed.' ) . EOL );
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-01-11 01:06:00 +01:00
// NOTREACHED
}
2018-02-09 06:08:01 +01:00
// Otherwise it's probably an openid.
try {
2018-10-09 19:58:58 +02:00
$openid = new LightOpenID ( $a -> getHostName ());
2018-02-09 06:08:01 +01:00
$openid -> identity = $openid_url ;
$_SESSION [ 'openid' ] = $openid_url ;
$_SESSION [ 'remember' ] = $remember ;
2018-10-19 20:11:27 +02:00
$openid -> returnUrl = $a -> getBaseURL ( true ) . '/openid' ;
System :: externalRedirect ( $openid -> authUrl ());
2018-02-09 06:08:01 +01:00
} catch ( Exception $e ) {
notice ( L10n :: t ( 'We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.' ) . '<br /><br >' . L10n :: t ( 'The error message was:' ) . ' ' . $e -> getMessage ());
}
}
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +01:00
/**
* Attempts to authenticate using login / password
*
* @ param string $username User name
* @ param string $password Clear password
* @ param bool $remember Whether to set the session remember flag
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-02-09 06:08:01 +01:00
*/
private static function passwordAuthentication ( $username , $password , $remember )
{
$record = null ;
$addon_auth = [
'username' => $username ,
'password' => $password ,
'authenticated' => 0 ,
'user_record' => null
];
2018-10-13 20:02:04 +02:00
$a = self :: getApp ();
2018-02-09 06:08:01 +01:00
/*
* An addon indicates successful login by setting 'authenticated' to non - zero value and returning a user record
* Addons should never set 'authenticated' except to indicate success - as hooks may be chained
* and later addons should not interfere with an earlier one that succeeded .
*/
2018-12-26 07:06:24 +01:00
Hook :: callAll ( 'authenticate' , $addon_auth );
2018-02-09 06:08:01 +01:00
try {
if ( $addon_auth [ 'authenticated' ]) {
$record = $addon_auth [ 'user_record' ];
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +01:00
if ( empty ( $record )) {
throw new Exception ( L10n :: t ( 'Login failed.' ));
}
} else {
2019-10-11 01:21:41 +02:00
$record = DBA :: selectFirst (
'user' ,
[],
2018-02-09 06:08:01 +01:00
[ 'uid' => User :: getIdFromPasswordAuthentication ( $username , $password )]
);
2018-01-11 01:06:00 +01:00
}
2018-02-09 06:08:01 +01:00
} catch ( Exception $e ) {
2019-01-07 21:32:06 +01:00
Logger :: warning ( 'authenticate: failed login attempt' , [ 'action' => 'login' , 'username' => Strings :: escapeTags ( $username ), 'ip' => $_SERVER [ 'REMOTE_ADDR' ]]);
2018-10-03 14:32:16 +02:00
info ( 'Login failed. Please check your credentials.' . EOL );
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-02-09 06:08:01 +01:00
}
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +01:00
if ( ! $remember ) {
2018-10-17 21:30:41 +02:00
Authentication :: setCookie ( 0 ); // 0 means delete on browser exit
2018-02-09 06:08:01 +01:00
}
2018-01-11 01:06:00 +01:00
2018-02-09 06:08:01 +01:00
// if we haven't failed up this point, log them in.
2019-05-26 22:15:38 +02:00
Session :: set ( 'remember' , $remember );
Session :: set ( 'last_login_date' , DateTimeFormat :: utcNow ());
2019-05-13 06:55:26 +02:00
Session :: setAuthenticatedForUser ( $a , $record , true , true );
2018-01-11 01:06:00 +01:00
2019-05-26 22:15:38 +02:00
$return_path = Session :: get ( 'return_path' , '' );
Session :: remove ( 'return_path' );
2018-02-09 06:08:01 +01:00
2018-10-19 23:55:11 +02:00
$a -> internalRedirect ( $return_path );
2018-01-11 01:06:00 +01:00
}
/**
* @ brief Tries to auth the user from the cookie or session
*
* @ todo Should be moved to Friendica\Core\Session when it ' s created
*/
public static function sessionAuth ()
{
2018-10-13 20:02:04 +02:00
$a = self :: getApp ();
2018-01-11 01:06:00 +01:00
// When the "Friendica" cookie is set, take the value to authenticate and renew the cookie.
if ( isset ( $_COOKIE [ " Friendica " ])) {
$data = json_decode ( $_COOKIE [ " Friendica " ]);
if ( isset ( $data -> uid )) {
2019-10-11 01:21:41 +02:00
$user = DBA :: selectFirst (
'user' ,
[],
2018-01-11 01:06:00 +01:00
[
'uid' => $data -> uid ,
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $user )) {
2019-10-11 01:21:41 +02:00
// Time safe comparision of the two hashes.
$validSession = hash_equals (
Authentication :: getCookieHashForUser ( $user ),
$data -> hash
);
if ( ! $validSession ) {
2018-10-29 22:20:46 +01:00
Logger :: log ( " Hash for user " . $data -> uid . " doesn't fit. " );
2018-10-17 21:30:41 +02:00
Authentication :: deleteSession ();
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-01-11 01:06:00 +01:00
}
// Renew the cookie
// Expires after 7 days by default,
// can be set via system.auth_cookie_lifetime
$authcookiedays = Config :: get ( 'system' , 'auth_cookie_lifetime' , 7 );
2018-10-17 21:30:41 +02:00
Authentication :: setCookie ( $authcookiedays * 24 * 60 * 60 , $user );
2018-01-11 01:06:00 +01:00
// Do the authentification if not done by now
if ( ! isset ( $_SESSION ) || ! isset ( $_SESSION [ 'authenticated' ])) {
2019-05-13 06:55:26 +02:00
Session :: setAuthenticatedForUser ( $a , $user );
2018-01-11 01:06:00 +01:00
if ( Config :: get ( 'system' , 'paranoia' )) {
$_SESSION [ 'addr' ] = $data -> ip ;
}
}
}
}
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_SESSION [ 'authenticated' ])) {
if ( ! empty ( $_SESSION [ 'visitor_id' ]) && empty ( $_SESSION [ 'uid' ])) {
2018-08-19 14:46:11 +02:00
$contact = DBA :: selectFirst ( 'contact' , [], [ 'id' => $_SESSION [ 'visitor_id' ]]);
if ( DBA :: isResult ( $contact )) {
self :: getApp () -> contact = $contact ;
2018-01-11 01:06:00 +01:00
}
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $_SESSION [ 'uid' ])) {
2018-01-11 01:06:00 +01:00
// already logged in user returning
$check = Config :: get ( 'system' , 'paranoia' );
// extra paranoia - if the IP changed, log them out
if ( $check && ( $_SESSION [ 'addr' ] != $_SERVER [ 'REMOTE_ADDR' ])) {
2018-10-29 22:20:46 +01:00
Logger :: log ( 'Session address changed. Paranoid setting in effect, blocking session. ' .
2018-01-11 01:06:00 +01:00
$_SESSION [ 'addr' ] . ' != ' . $_SERVER [ 'REMOTE_ADDR' ]);
2018-10-17 21:30:41 +02:00
Authentication :: deleteSession ();
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-01-11 01:06:00 +01:00
}
2019-10-11 01:21:41 +02:00
$user = DBA :: selectFirst (
'user' ,
[],
2018-01-11 01:06:00 +01:00
[
'uid' => $_SESSION [ 'uid' ],
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $user )) {
2018-10-17 21:30:41 +02:00
Authentication :: deleteSession ();
2018-10-19 20:11:27 +02:00
$a -> internalRedirect ();
2018-01-11 01:06:00 +01:00
}
// 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 ;
2018-07-10 14:27:56 +02:00
if ( empty ( $_SESSION [ 'last_login_date' ])) {
2018-01-27 03:38:34 +01:00
$_SESSION [ 'last_login_date' ] = DateTimeFormat :: utcNow ();
2018-01-11 01:06:00 +01:00
}
2018-01-27 03:38:34 +01:00
if ( strcmp ( DateTimeFormat :: utc ( 'now - 12 hours' ), $_SESSION [ 'last_login_date' ]) > 0 ) {
$_SESSION [ 'last_login_date' ] = DateTimeFormat :: utcNow ();
2018-01-11 01:06:00 +01:00
$login_refresh = true ;
}
2019-05-13 06:55:26 +02:00
Session :: setAuthenticatedForUser ( $a , $user , false , false , $login_refresh );
2018-01-11 01:06:00 +01:00
}
}
}
/**
* @ brief Wrapper for adding a login box .
*
2019-01-06 22:06:53 +01:00
* @ param string $return_path The path relative to the base the user should be sent
* back to after login completes
* @ param bool $register If $register == true provide a registration link .
* This will most always depend on the value of config . register_policy .
* @ param array $hiddens optional
2018-01-11 01:06:00 +01:00
*
* @ return string Returns the complete html for inserting into the page
*
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-01-11 01:06:00 +01:00
* @ hooks 'login_hook' string $o
*/
2018-10-19 23:55:11 +02:00
public static function form ( $return_path = null , $register = false , $hiddens = [])
2018-01-11 01:06:00 +01:00
{
$a = self :: getApp ();
$o = '' ;
$reg = false ;
2019-07-15 15:11:21 +02:00
if ( $register && intval ( $a -> getConfig () -> get ( 'config' , 'register_policy' )) !== Register :: CLOSED ) {
2018-01-15 14:05:12 +01:00
$reg = [
2018-01-22 15:54:13 +01:00
'title' => L10n :: t ( 'Create a New Account' ),
'desc' => L10n :: t ( 'Register' )
2018-01-15 14:05:12 +01:00
];
2018-01-11 01:06:00 +01:00
}
$noid = Config :: get ( 'system' , 'no_openid' );
2018-10-19 23:55:11 +02:00
if ( is_null ( $return_path )) {
$return_path = $a -> query_string ;
2018-01-11 01:06:00 +01:00
}
if ( local_user ()) {
2018-10-31 15:44:06 +01:00
$tpl = Renderer :: getMarkupTemplate ( 'logout.tpl' );
2018-01-11 01:06:00 +01:00
} else {
2018-10-31 15:35:50 +01:00
$a -> page [ 'htmlhead' ] .= Renderer :: replaceMacros (
2018-10-31 15:44:06 +01:00
Renderer :: getMarkupTemplate ( 'login_head.tpl' ),
2018-01-11 01:06:00 +01:00
[
2018-10-09 19:58:58 +02:00
'$baseurl' => $a -> getBaseURL ( true )
2018-01-11 01:06:00 +01:00
]
);
2018-10-31 15:44:06 +01:00
$tpl = Renderer :: getMarkupTemplate ( 'login.tpl' );
2018-10-19 23:55:11 +02:00
$_SESSION [ 'return_path' ] = $return_path ;
2018-01-11 01:06:00 +01:00
}
2018-10-31 15:35:50 +01:00
$o .= Renderer :: replaceMacros (
2018-01-11 01:06:00 +01:00
$tpl ,
[
2018-10-09 19:58:58 +02:00
'$dest_url' => self :: getApp () -> getBaseURL ( true ) . '/login' ,
2018-01-22 15:54:13 +01:00
'$logout' => L10n :: t ( 'Logout' ),
'$login' => L10n :: t ( 'Login' ),
2018-01-11 01:06:00 +01:00
2019-10-11 01:21:41 +02:00
'$lname' => [ 'username' , L10n :: t ( 'Nickname or Email: ' ), '' , '' ],
2018-01-22 15:54:13 +01:00
'$lpassword' => [ 'password' , L10n :: t ( 'Password: ' ), '' , '' ],
'$lremember' => [ 'remember' , L10n :: t ( 'Remember me' ), 0 , '' ],
2018-01-11 01:06:00 +01:00
'$openid' => ! $noid ,
2019-10-11 01:21:41 +02:00
'$lopenid' => [ 'openid_url' , L10n :: t ( 'Or login using OpenID: ' ), '' , '' ],
2018-01-11 01:06:00 +01:00
'$hiddens' => $hiddens ,
'$register' => $reg ,
2018-01-22 15:54:13 +01:00
'$lostpass' => L10n :: t ( 'Forgot your password?' ),
'$lostlink' => L10n :: t ( 'Password Reset' ),
2018-01-11 01:06:00 +01:00
2018-01-22 15:54:13 +01:00
'$tostitle' => L10n :: t ( 'Website Terms of Service' ),
'$toslink' => L10n :: t ( 'terms of service' ),
2018-01-11 01:06:00 +01:00
2018-01-22 15:54:13 +01:00
'$privacytitle' => L10n :: t ( 'Website Privacy Policy' ),
'$privacylink' => L10n :: t ( 'privacy policy' ),
2018-01-11 01:06:00 +01:00
]
);
2018-12-26 07:06:24 +01:00
Hook :: callAll ( 'login_hook' , $o );
2018-01-11 01:06:00 +01:00
return $o ;
}
}