2018-01-11 00:06:00 +00:00
< ? php
2018-01-21 18:33:59 +00:00
/**
* @ file src / Module / Login . php
*/
2018-01-11 00:06:00 +00:00
namespace Friendica\Module ;
2018-07-20 02:15:21 +00:00
use Exception ;
2018-01-11 00:06:00 +00:00
use Friendica\BaseModule ;
2018-10-17 12:19:58 +00:00
use Friendica\Core\Authentication ;
2018-01-11 00:06:00 +00:00
use Friendica\Core\Config ;
2018-12-26 06:06:24 +00:00
use Friendica\Core\Hook ;
2018-01-21 18:33:59 +00:00
use Friendica\Core\L10n ;
2018-10-29 21:20:46 +00:00
use Friendica\Core\Logger ;
2018-10-31 14:35:50 +00:00
use Friendica\Core\Renderer ;
2018-10-13 18:02:04 +00:00
use Friendica\Core\System ;
2018-07-20 12:19:26 +00:00
use Friendica\Database\DBA ;
2018-01-11 00:06:00 +00:00
use Friendica\Model\User ;
2018-01-27 02:38:34 +00:00
use Friendica\Util\DateTimeFormat ;
2018-01-27 04:51:41 +00:00
use Friendica\Util\Network ;
2018-11-08 15:14:37 +00:00
use Friendica\Util\Strings ;
2018-01-25 02:08:45 +00:00
use LightOpenID ;
2018-01-11 00:06:00 +00:00
/**
* Login module
*
2018-09-15 23:28:38 +00:00
* @ author Hypolite Petovan < hypolite @ mrpetovan . com >
2018-01-11 00:06:00 +00:00
*/
class Login extends BaseModule
{
public static function content ()
{
$a = self :: getApp ();
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_SESSION [ 'theme' ])) {
2018-01-11 00:06:00 +00:00
unset ( $_SESSION [ 'theme' ]);
}
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_SESSION [ 'mobile-theme' ])) {
2018-01-11 00:06:00 +00:00
unset ( $_SESSION [ 'mobile-theme' ]);
}
if ( local_user ()) {
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-01-11 00:06:00 +00:00
}
2018-11-13 18:10:37 +00:00
return self :: form ( defaults ( $_SESSION , 'return_path' , null ), intval ( Config :: get ( 'config' , 'register_policy' )) !== REGISTER_CLOSED );
2018-01-11 00:06:00 +00:00
}
public static function post ()
{
2019-01-13 18:03:13 +00:00
$return_path = defaults ( $_SESSION , 'return_path' , '' );
2018-01-11 00:06:00 +00:00
session_unset ();
2018-10-19 21:55:11 +00:00
$_SESSION [ 'return_path' ] = $return_path ;
2019-01-13 18:03:13 +00:00
2018-01-11 00:06:00 +00:00
// OpenId Login
if (
2018-02-09 05:08:01 +00:00
empty ( $_POST [ 'password' ])
2018-01-11 00:06:00 +00:00
&& (
2018-02-09 05:08:01 +00:00
! empty ( $_POST [ 'openid_url' ])
|| ! empty ( $_POST [ 'username' ])
2018-01-11 00:06:00 +00:00
)
) {
2018-02-09 05:08:01 +00:00
$openid_url = trim ( defaults ( $_POST , 'openid_url' , $_POST [ 'username' ]));
2018-01-11 00:06:00 +00:00
2018-02-09 05:08:01 +00:00
self :: openIdAuthentication ( $openid_url , ! empty ( $_POST [ 'remember' ]));
}
2018-01-11 00:06:00 +00:00
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_POST [ 'auth-params' ]) && $_POST [ 'auth-params' ] === 'login' ) {
2018-02-09 05:08:01 +00:00
self :: passwordAuthentication (
trim ( $_POST [ 'username' ]),
trim ( $_POST [ 'password' ]),
! empty ( $_POST [ 'remember' ])
);
}
}
2018-01-11 00:06:00 +00:00
2018-02-09 05:08:01 +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-01-06 21:06:53 +00:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-02-09 05:08:01 +00:00
*/
private static function openIdAuthentication ( $openid_url , $remember )
{
$noid = Config :: get ( 'system' , 'no_openid' );
2018-10-13 18:02:04 +00:00
$a = self :: getApp ();
2018-02-09 05:08:01 +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 )) {
notice ( L10n :: t ( 'Login failed.' ) . EOL );
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-01-11 00:06:00 +00:00
// NOTREACHED
}
2018-02-09 05:08:01 +00:00
// Otherwise it's probably an openid.
try {
2018-10-09 17:58:58 +00:00
$openid = new LightOpenID ( $a -> getHostName ());
2018-02-09 05:08:01 +00:00
$openid -> identity = $openid_url ;
$_SESSION [ 'openid' ] = $openid_url ;
$_SESSION [ 'remember' ] = $remember ;
2018-10-19 18:11:27 +00:00
$openid -> returnUrl = $a -> getBaseURL ( true ) . '/openid' ;
System :: externalRedirect ( $openid -> authUrl ());
2018-02-09 05:08:01 +00: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 00:06:00 +00:00
2018-02-09 05:08:01 +00: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 21:06:53 +00:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-02-09 05:08:01 +00:00
*/
private static function passwordAuthentication ( $username , $password , $remember )
{
$record = null ;
$addon_auth = [
'username' => $username ,
'password' => $password ,
'authenticated' => 0 ,
'user_record' => null
];
2018-10-13 18:02:04 +00:00
$a = self :: getApp ();
2018-02-09 05:08:01 +00: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 06:06:24 +00:00
Hook :: callAll ( 'authenticate' , $addon_auth );
2018-02-09 05:08:01 +00:00
try {
if ( $addon_auth [ 'authenticated' ]) {
$record = $addon_auth [ 'user_record' ];
2018-01-11 00:06:00 +00:00
2018-02-09 05:08:01 +00:00
if ( empty ( $record )) {
throw new Exception ( L10n :: t ( 'Login failed.' ));
}
} else {
2018-07-20 12:19:26 +00:00
$record = DBA :: selectFirst ( 'user' , [],
2018-02-09 05:08:01 +00:00
[ 'uid' => User :: getIdFromPasswordAuthentication ( $username , $password )]
);
2018-01-11 00:06:00 +00:00
}
2018-02-09 05:08:01 +00:00
} catch ( Exception $e ) {
2019-01-07 20:32:06 +00:00
Logger :: warning ( 'authenticate: failed login attempt' , [ 'action' => 'login' , 'username' => Strings :: escapeTags ( $username ), 'ip' => $_SERVER [ 'REMOTE_ADDR' ]]);
2018-10-03 12:32:16 +00:00
info ( 'Login failed. Please check your credentials.' . EOL );
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-02-09 05:08:01 +00:00
}
2018-01-11 00:06:00 +00:00
2018-02-09 05:08:01 +00:00
if ( ! $remember ) {
2018-10-17 19:30:41 +00:00
Authentication :: setCookie ( 0 ); // 0 means delete on browser exit
2018-02-09 05:08:01 +00:00
}
2018-01-11 00:06:00 +00:00
2018-02-09 05:08:01 +00:00
// if we haven't failed up this point, log them in.
$_SESSION [ 'remember' ] = $remember ;
$_SESSION [ 'last_login_date' ] = DateTimeFormat :: utcNow ();
2018-10-17 19:30:41 +00:00
Authentication :: setAuthenticatedSessionForUser ( $record , true , true );
2018-01-11 00:06:00 +00:00
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_SESSION [ 'return_path' ])) {
2018-10-19 21:55:11 +00:00
$return_path = $_SESSION [ 'return_path' ];
unset ( $_SESSION [ 'return_path' ]);
2018-02-09 05:08:01 +00:00
} else {
2018-10-19 21:55:11 +00:00
$return_path = '' ;
2018-01-11 00:06:00 +00:00
}
2018-02-09 05:08:01 +00:00
2018-10-19 21:55:11 +00:00
$a -> internalRedirect ( $return_path );
2018-01-11 00:06:00 +00: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 18:02:04 +00:00
$a = self :: getApp ();
2018-01-11 00:06:00 +00: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 )) {
2018-07-20 12:19:26 +00:00
$user = DBA :: selectFirst ( 'user' , [],
2018-01-11 00:06:00 +00:00
[
'uid' => $data -> uid ,
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $user )) {
2018-10-17 19:30:41 +00:00
if ( $data -> hash != Authentication :: getCookieHashForUser ( $user )) {
2018-10-29 21:20:46 +00:00
Logger :: log ( " Hash for user " . $data -> uid . " doesn't fit. " );
2018-10-17 19:30:41 +00:00
Authentication :: deleteSession ();
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-01-11 00:06:00 +00: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 19:30:41 +00:00
Authentication :: setCookie ( $authcookiedays * 24 * 60 * 60 , $user );
2018-01-11 00:06:00 +00:00
// Do the authentification if not done by now
if ( ! isset ( $_SESSION ) || ! isset ( $_SESSION [ 'authenticated' ])) {
2018-10-17 19:30:41 +00:00
Authentication :: setAuthenticatedSessionForUser ( $user );
2018-01-11 00:06:00 +00:00
if ( Config :: get ( 'system' , 'paranoia' )) {
$_SESSION [ 'addr' ] = $data -> ip ;
}
}
}
}
}
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_SESSION [ 'authenticated' ])) {
if ( ! empty ( $_SESSION [ 'visitor_id' ]) && empty ( $_SESSION [ 'uid' ])) {
2018-08-19 12:46:11 +00:00
$contact = DBA :: selectFirst ( 'contact' , [], [ 'id' => $_SESSION [ 'visitor_id' ]]);
if ( DBA :: isResult ( $contact )) {
self :: getApp () -> contact = $contact ;
2018-01-11 00:06:00 +00:00
}
}
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_SESSION [ 'uid' ])) {
2018-01-11 00:06:00 +00: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 21:20:46 +00:00
Logger :: log ( 'Session address changed. Paranoid setting in effect, blocking session. ' .
2018-01-11 00:06:00 +00:00
$_SESSION [ 'addr' ] . ' != ' . $_SERVER [ 'REMOTE_ADDR' ]);
2018-10-17 19:30:41 +00:00
Authentication :: deleteSession ();
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-01-11 00:06:00 +00:00
}
2018-07-20 12:19:26 +00:00
$user = DBA :: selectFirst ( 'user' , [],
2018-01-11 00:06:00 +00:00
[
'uid' => $_SESSION [ 'uid' ],
'blocked' => false ,
'account_expired' => false ,
'account_removed' => false ,
'verified' => true ,
]
);
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $user )) {
2018-10-17 19:30:41 +00:00
Authentication :: deleteSession ();
2018-10-19 18:11:27 +00:00
$a -> internalRedirect ();
2018-01-11 00:06:00 +00: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 12:27:56 +00:00
if ( empty ( $_SESSION [ 'last_login_date' ])) {
2018-01-27 02:38:34 +00:00
$_SESSION [ 'last_login_date' ] = DateTimeFormat :: utcNow ();
2018-01-11 00:06:00 +00:00
}
2018-01-27 02:38:34 +00:00
if ( strcmp ( DateTimeFormat :: utc ( 'now - 12 hours' ), $_SESSION [ 'last_login_date' ]) > 0 ) {
$_SESSION [ 'last_login_date' ] = DateTimeFormat :: utcNow ();
2018-01-11 00:06:00 +00:00
$login_refresh = true ;
}
2018-10-17 19:30:41 +00:00
Authentication :: setAuthenticatedSessionForUser ( $user , false , false , $login_refresh );
2018-01-11 00:06:00 +00:00
}
}
}
/**
* @ brief Wrapper for adding a login box .
*
2019-01-06 21:06:53 +00: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 00:06:00 +00:00
*
* @ return string Returns the complete html for inserting into the page
*
2019-01-06 21:06:53 +00:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-01-11 00:06:00 +00:00
* @ hooks 'login_hook' string $o
*/
2018-10-19 21:55:11 +00:00
public static function form ( $return_path = null , $register = false , $hiddens = [])
2018-01-11 00:06:00 +00:00
{
$a = self :: getApp ();
$o = '' ;
$reg = false ;
if ( $register ) {
2018-01-15 13:05:12 +00:00
$reg = [
2018-01-22 14:54:13 +00:00
'title' => L10n :: t ( 'Create a New Account' ),
'desc' => L10n :: t ( 'Register' )
2018-01-15 13:05:12 +00:00
];
2018-01-11 00:06:00 +00:00
}
$noid = Config :: get ( 'system' , 'no_openid' );
2018-10-19 21:55:11 +00:00
if ( is_null ( $return_path )) {
$return_path = $a -> query_string ;
2018-01-11 00:06:00 +00:00
}
if ( local_user ()) {
2018-10-31 14:44:06 +00:00
$tpl = Renderer :: getMarkupTemplate ( 'logout.tpl' );
2018-01-11 00:06:00 +00:00
} else {
2018-10-31 14:35:50 +00:00
$a -> page [ 'htmlhead' ] .= Renderer :: replaceMacros (
2018-10-31 14:44:06 +00:00
Renderer :: getMarkupTemplate ( 'login_head.tpl' ),
2018-01-11 00:06:00 +00:00
[
2018-10-09 17:58:58 +00:00
'$baseurl' => $a -> getBaseURL ( true )
2018-01-11 00:06:00 +00:00
]
);
2018-10-31 14:44:06 +00:00
$tpl = Renderer :: getMarkupTemplate ( 'login.tpl' );
2018-10-19 21:55:11 +00:00
$_SESSION [ 'return_path' ] = $return_path ;
2018-01-11 00:06:00 +00:00
}
2018-10-31 14:35:50 +00:00
$o .= Renderer :: replaceMacros (
2018-01-11 00:06:00 +00:00
$tpl ,
[
2018-10-09 17:58:58 +00:00
'$dest_url' => self :: getApp () -> getBaseURL ( true ) . '/login' ,
2018-01-22 14:54:13 +00:00
'$logout' => L10n :: t ( 'Logout' ),
'$login' => L10n :: t ( 'Login' ),
2018-01-11 00:06:00 +00:00
2018-01-22 14:54:13 +00:00
'$lname' => [ 'username' , L10n :: t ( 'Nickname or Email: ' ) , '' , '' ],
'$lpassword' => [ 'password' , L10n :: t ( 'Password: ' ), '' , '' ],
'$lremember' => [ 'remember' , L10n :: t ( 'Remember me' ), 0 , '' ],
2018-01-11 00:06:00 +00:00
'$openid' => ! $noid ,
2018-01-22 14:54:13 +00:00
'$lopenid' => [ 'openid_url' , L10n :: t ( 'Or login using OpenID: ' ), '' , '' ],
2018-01-11 00:06:00 +00:00
'$hiddens' => $hiddens ,
'$register' => $reg ,
2018-01-22 14:54:13 +00:00
'$lostpass' => L10n :: t ( 'Forgot your password?' ),
'$lostlink' => L10n :: t ( 'Password Reset' ),
2018-01-11 00:06:00 +00:00
2018-01-22 14:54:13 +00:00
'$tostitle' => L10n :: t ( 'Website Terms of Service' ),
'$toslink' => L10n :: t ( 'terms of service' ),
2018-01-11 00:06:00 +00:00
2018-01-22 14:54:13 +00:00
'$privacytitle' => L10n :: t ( 'Website Privacy Policy' ),
'$privacylink' => L10n :: t ( 'privacy policy' ),
2018-01-11 00:06:00 +00:00
]
);
2018-12-26 06:06:24 +00:00
Hook :: callAll ( 'login_hook' , $o );
2018-01-11 00:06:00 +00:00
return $o ;
}
}