hedera-web/web/service.php

355 lines
7.8 KiB
PHP
Raw Normal View History

<?php
namespace Vn\Web;
2017-05-02 12:33:48 +00:00
use Vn\Db;
use Vn\Lib\Locale;
2016-09-23 22:47:34 +00:00
use Vn\Lib\UserException;
2016-10-04 15:27:49 +00:00
const MIN = 60;
const HOUR = 60 * MIN;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
/**
* Thrown when user credentials could not be fetched.
*/
2016-09-23 22:47:34 +00:00
class SessionExpiredException extends UserException {}
/**
* Thrown when user credentials are invalid.
*/
2016-09-23 22:47:34 +00:00
class BadLoginException extends UserException {}
/**
* Thrown when client version is outdated.
*/
2016-09-23 22:47:34 +00:00
class OutdatedVersionException extends UserException {}
/**
* Main class for web applications.
*/
abstract class Service
{
protected $app;
2016-09-23 22:47:34 +00:00
protected $db;
protected $userDb = NULL;
function __construct ($app)
{
$this->app = $app;
}
function init ()
{
$this->db = $this->app->getSysConn ();
}
/**
* Starts the user session.
*/
function startSession ()
{
$db = $this->app->getSysConn ();
2016-09-19 06:40:18 +00:00
2016-09-24 14:32:31 +00:00
ini_set ('session.cookie_secure', $this->isHttps ());
2016-09-23 22:47:34 +00:00
ini_set ('session.hash_function', 'sha256');
2016-09-19 06:40:18 +00:00
session_set_save_handler (new DbSessionHandler ($db));
session_start ();
2016-09-19 06:40:18 +00:00
// Setting the locale
if (isset ($_SERVER['HTTP_ACCEPT_LANGUAGE']))
2016-09-19 06:40:18 +00:00
if (!isset ($_SESSION['httpLanguage'])
|| $_SESSION['httpLanguage'] != $_SERVER['HTTP_ACCEPT_LANGUAGE'])
{
2016-09-19 06:40:18 +00:00
$_SESSION['httpLanguage'] = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$regexp = '/([a-z]{1,4})(?:-[a-z]{1,4})?\s*(?:;\s*q\s*=\s*(?:1|0\.[0-9]+))?,?/i';
preg_match_all ($regexp, $_SERVER['HTTP_ACCEPT_LANGUAGE'], $languages);
foreach ($languages[1] as $lang)
2016-11-08 07:00:02 +00:00
if (TRUE || stream_resolve_include_path ("locale/$lang"))
{
$_SESSION['lang'] = $lang;
break;
}
}
if (!isset ($_SESSION['lang']))
$_SESSION['lang'] = NULL;
2016-11-08 07:00:02 +00:00
Locale::set ($_SESSION['lang']);
Locale::addPath ('vn/web');
// Registering the visit
2016-10-17 12:36:52 +00:00
if (isset ($_COOKIE['PHPSESSID'])
|| isset ($_SESSION['access'])
2017-12-13 18:28:54 +00:00
|| isset ($_SESSION['skipVisit'])
|| !isset ($_SERVER['HTTP_USER_AGENT']))
return;
$agent = $_SERVER['HTTP_USER_AGENT'];
$browser = get_browser ($agent, TRUE);
2016-10-16 14:16:08 +00:00
if (!empty ($browser['crawler']))
{
$_SESSION['skipVisit'] = TRUE;
return;
}
if (isset ($_SERVER['REMOTE_ADDR']))
$ip = ip2long ($_SERVER['REMOTE_ADDR']);
$row = $db->getRow (
2016-09-19 06:40:18 +00:00
'CALL visitRegister (#, #, #, #, #, #, #, #, #)',
[
2016-09-19 06:40:18 +00:00
nullIf ($_COOKIE, 'vnVisit')
,nullIf ($browser, 'platform')
,nullIf ($browser, 'browser')
,nullIf ($browser, 'version')
,nullIf ($browser, 'javascript')
,nullIf ($browser, 'cookies')
,isset ($agent) ? $agent : NULL
,isset ($ip) && $ip ? $ip : NULL
,nullIf ($_SERVER, 'HTTP_REFERER')
]
);
if (isset ($row['access']))
{
2016-09-19 06:40:18 +00:00
setcookie ('vnVisit', $row['visit'], time () + 31536000); // 1 Year
$_SESSION['access'] = $row['access'];
}
else
$_SESSION['skipVisit'] = TRUE;
}
2016-09-20 18:36:22 +00:00
/**
* Tries to retrieve user credentials from many sources such as POST,
* SESSION or COOKIES. If $_POST['remember'] is defined the user credentials
* are saved on the client brownser for future logins, cookies names are
* 'vn_user' for the user name and 'vn_pass' for user password, the
* password is encoded using base64_encode() function and should be decoded
* using base64_decode().
*
* return Db\Conn The database connection
*/
2016-09-20 18:36:22 +00:00
function login ()
{
2016-09-23 22:47:34 +00:00
$db = $this->db;
$anonymousUser = FALSE;
2016-09-20 18:36:22 +00:00
if (isset ($_POST['user']) && isset ($_POST['password']))
{
$user = strtolower ($_POST['user']);
try {
$db->query ('CALL account.userLogin (#, #)',
[$user, $_POST['password']]);
}
2017-05-02 12:33:48 +00:00
catch (Db\Exception $e)
2016-09-20 18:36:22 +00:00
{
2016-10-20 10:44:32 +00:00
if ($e->getMessage () == 'INVALID_CREDENTIALS')
{
sleep (3);
2016-10-20 10:44:32 +00:00
throw new BadLoginException ();
}
2016-10-20 10:44:32 +00:00
else
throw $e;
2016-09-20 18:36:22 +00:00
}
}
else
{
if (isset ($_POST['token']) || isset ($_GET['token']))
{
if (isset ($_POST['token']))
$token = $_POST['token'];
if (isset ($_GET['token']))
$token = $_GET['token'];
2016-09-24 14:32:31 +00:00
$key = $db->getValue ('SELECT jwtKey FROM config');
try {
$jwtPayload = Jwt::decode ($token, $key);
}
catch (\Exception $e)
{
throw new BadLoginException ($e->getMessage ());
}
2016-09-20 18:36:22 +00:00
$expiration = $jwtPayload['exp'];
2016-09-24 14:32:31 +00:00
if (empty ($expiration) || $expiration <= time())
2016-09-20 18:36:22 +00:00
throw new SessionExpiredException ();
2016-09-24 14:32:31 +00:00
$user = $jwtPayload['sub'];
2016-10-14 10:58:35 +00:00
if (!empty ($jwtPayload['recover']))
$db->query (
2016-10-14 10:58:35 +00:00
'UPDATE account.user SET recoverPass = TRUE
WHERE name = #',
[$user]
);
2016-10-14 10:58:35 +00:00
}
2016-09-20 18:36:22 +00:00
else
{
2016-09-20 18:36:22 +00:00
$user = $db->getValue ('SELECT guest_user FROM config');
$anonymousUser = TRUE;
}
2016-09-20 18:36:22 +00:00
$db->query ('CALL account.userLoginWithName (#)', [$user]);
}
$userChanged = !$anonymousUser
&& (empty ($_SESSION['user']) || $_SESSION['user'] != $user);
2016-09-20 18:36:22 +00:00
$_SESSION['user'] = $user;
// Registering the user access
if (isset ($_SESSION['access']) && $userChanged)
2016-09-20 18:36:22 +00:00
$db->query (
'CALL visitUserNew (#, #)',
[$_SESSION['access'], session_id ()]
);
}
/**
* Logouts the current user. Cleans the last saved used credentials.
*/
2016-09-20 18:36:22 +00:00
function logout ()
{
unset ($_SESSION['user']);
}
2016-09-23 22:47:34 +00:00
/**
* Creates or returns a database connection where the authenticated user
* is the role of the current logged user.
2016-09-23 22:47:34 +00:00
*
* @return {Db\Conn} The database connection
*/
2016-09-23 22:47:34 +00:00
function getUserDb ($user)
{
if ($this->userDb)
return $this->userDb;
$row = $this->db->getObject (
'SELECT r.name, rc.mysqlPassword, uc.loginKey
FROM account.user u
JOIN account.role r ON r.id = u.role
JOIN account.roleConfig rc ON TRUE
JOIN account.userConfig uc ON TRUE
WHERE u.name = #',
[$user]
);
$userName = "z-{$row->name}";
$password = base64_decode ($row->mysqlPassword);
$userDb = $this->app->createConnection ($userName, $password, TRUE);
$userDb->query ('CALL account.userLoginWithKey (#, #)', [$user, $row->loginKey]);
return $userDb;
2016-09-23 22:47:34 +00:00
}
2016-10-04 15:27:49 +00:00
/**
* Generates a JWT authentication token for the specified $user.
*
* @param {string} $user The user name
* @param {boolean} $remember Wether to create long live token
2016-10-14 10:58:35 +00:00
* @param {boolean} $recover Wether to enable recovery mode on login
2016-10-04 15:27:49 +00:00
* @return {string} The JWT generated token
*/
2016-10-14 10:58:35 +00:00
function createToken ($user, $remember = FALSE, $recover = FALSE)
2016-10-04 15:27:49 +00:00
{
if ($remember)
$tokenLife = WEEK;
else
$tokenLife = 30 * MIN;
$payload = [
'sub' => $user,
'exp' => time () + $tokenLife
];
2016-10-16 14:16:08 +00:00
if ($recover)
2016-10-14 10:58:35 +00:00
$payload['recover'] = 'TRUE';
2016-10-04 15:27:49 +00:00
$key = $this->db->getValue ('SELECT jwtKey FROM config');
return Jwt::encode ($payload, $key);
}
2016-09-23 22:47:34 +00:00
/**
* Obtains the application version number. It is extracted and
* cached from package.json file.
*
* @return string The version number
*/
function getVersion ()
2016-09-23 22:47:34 +00:00
{
$appName = $this->app->getName ();
$version = apc_fetch("$appName.version", $success);
2016-09-23 22:47:34 +00:00
if (!$success)
2016-09-23 22:47:34 +00:00
{
if (file_exists ('package.json'))
{
$package = json_decode (file_get_contents ('package.json'));
$version = $package->version;
}
else
$version = '0.0.0';
2016-09-23 22:47:34 +00:00
apc_store ("$appName.version", $version);
}
2017-05-02 12:33:48 +00:00
return $version;
}
2017-05-22 07:49:05 +00:00
/**
* Checks the client version.
*/
function checkVersion ()
{
if (!empty ($_COOKIE['vnVersion']))
$clientVersion = $_COOKIE['vnVersion'];
2016-09-23 22:47:34 +00:00
if (isset ($clientVersion)
&& $clientVersion < $this->getVersion ())
throw new OutdatedVersionException ();
2016-09-23 22:47:34 +00:00
}
/**
* Checks if the HTTP connection is secure.
*
* @return boolean Return %TRUE if its secure, %FALSE otherwise
*/
function isHttps ()
{
return isset ($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on';
}
/**
* Returns the current URI without the GET part.
2016-09-24 14:32:31 +00:00
*
* @return string The current URI
*/
function getUri ()
{
return "{$_SERVER['SERVER_NAME']}{$_SERVER['REQUEST_URI']}";
}
2016-09-24 14:32:31 +00:00
/**
* Returns the current URL without the GET part.
2016-09-24 14:32:31 +00:00
*
* @return string The current URL
*/
function getUrl ()
2016-09-24 14:32:31 +00:00
{
$proto = $this->isHttps () ? 'https' : 'http';
return "$proto://{$this->getUri()}";
2016-09-24 14:32:31 +00:00
}
}