0
1
Fork 0
hedera-web-mindshore/web/service.php

356 lines
8.0 KiB
PHP
Raw Normal View History

<?php
namespace Vn\Web;
2022-05-09 13:52:29 +00:00
include __DIR__.'/uid.php';
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 {}
2022-05-05 13:56:17 +00:00
/**
* Thrown when user credentials are invalid.
*/
class ForbiddenException extends UserException {}
/**
* Thrown when user credentials are invalid.
*/
class UserDisabledException 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.
*/
2018-05-23 10:14:20 +00:00
abstract class Service {
protected $app;
2016-09-23 22:47:34 +00:00
protected $db;
protected $userDb = NULL;
2018-05-23 10:14:20 +00:00
function __construct($app) {
$this->app = $app;
}
2018-05-23 10:14:20 +00:00
function init() {
$this->db = $this->app->getSysConn();
}
/**
* Starts the user session.
*/
2018-05-23 10:14:20 +00:00
function startSession() {
$db = $this->app->getSysConn();
2016-09-19 06:40:18 +00:00
2018-05-23 10:14:20 +00:00
ini_set('session.cookie_secure', $this->isHttps());
ini_set('session.hash_function', 'sha256');
2016-09-19 06:40:18 +00:00
2018-05-23 10:14:20 +00:00
session_set_save_handler(new DbSessionHandler($db));
session_start();
2016-09-19 06:40:18 +00:00
// Setting the locale
2018-05-23 10:14:20 +00:00
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
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';
2018-05-23 10:14:20 +00:00
preg_match_all($regexp, $_SERVER['HTTP_ACCEPT_LANGUAGE'], $languages);
foreach ($languages[1] as $lang)
2018-05-23 10:14:20 +00:00
if (TRUE || stream_resolve_include_path("locale/$lang")) {
$_SESSION['lang'] = $lang;
break;
}
}
2018-05-23 10:14:20 +00:00
if (!isset($_SESSION['lang']))
$_SESSION['lang'] = NULL;
2016-11-08 07:00:02 +00:00
2018-05-23 10:14:20 +00:00
Locale::set($_SESSION['lang']);
Locale::addPath('vn/web');
// Registering the visit
2018-05-23 10:14:20 +00:00
if (isset($_COOKIE['PHPSESSID'])
|| isset($_SESSION['access'])
|| isset($_SESSION['skipVisit'])
|| !isset($_SERVER['HTTP_USER_AGENT']))
return;
$agent = $_SERVER['HTTP_USER_AGENT'];
2018-05-23 10:14:20 +00:00
$browser = get_browser($agent, TRUE);
2018-05-23 10:14:20 +00:00
if (!empty($browser['crawler'])) {
$_SESSION['skipVisit'] = TRUE;
return;
}
2018-05-23 10:14:20 +00:00
if (isset($_SERVER['REMOTE_ADDR']))
$ip = ip2long($_SERVER['REMOTE_ADDR']);
2018-05-23 10:14:20 +00:00
$row = $db->getRow(
2019-05-21 14:16:27 +00:00
'CALL visit_register(#, #, #, #, #, #, #, #, #)',
[
2018-05-23 10:14:20 +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')
]
);
2018-05-23 10:14:20 +00:00
if (isset($row['access'])) {
$domain = explode(':', $_SERVER['HTTP_HOST'])[0];
2022-06-18 21:04:34 +00:00
setcookie('vnVisit', $row['visit'], [
'expires' => time() + 31536000, // 1 Year
2022-11-11 14:32:45 +00:00
//'samesite' => 'Lax',
2022-11-11 14:40:07 +00:00
//'domain' => $domain
2022-06-18 21:04:34 +00:00
]);
$_SESSION['access'] = $row['access'];
2018-05-23 11:09:55 +00:00
} else
$_SESSION['skipVisit'] = TRUE;
}
2016-09-20 18:36:22 +00:00
/**
2019-07-02 08:48:14 +00:00
* Authenticates the user with it's credentials or token.
2016-09-20 18:36:22 +00:00
*
* return Db\Conn The database connection
*/
2018-05-23 10:14:20 +00:00
function login() {
2016-09-23 22:47:34 +00:00
$db = $this->db;
2022-05-05 13:56:17 +00:00
$anonymousUser = TRUE;
2022-05-20 12:20:34 +00:00
$token = NULL;
2022-05-05 13:56:17 +00:00
2022-05-20 12:20:34 +00:00
if (!empty($_SERVER['HTTP_AUTHORIZATION']))
$token = $_SERVER['HTTP_AUTHORIZATION'];
if (!empty($_GET['access_token']))
$token = $_GET['access_token'];
if (isset($token)) {
2022-05-05 13:56:17 +00:00
$userId = $db->getValue(
'SELECT userId FROM salix.AccessToken
WHERE id = #
AND NOW() <= TIMESTAMPADD(SECOND, ttl, created)',
2022-05-20 12:20:34 +00:00
[$token]
2019-07-02 08:48:14 +00:00
);
2022-05-05 13:56:17 +00:00
if (!$userId)
throw new SessionExpiredException();
2022-05-05 13:56:17 +00:00
$anonymousUser = FALSE;
$user = $db->getValue(
'SELECT `name` FROM account.user WHERE id = #',
[$userId]
);
} else
$user = $db->getValue('SELECT guestUser FROM config');
2016-09-20 18:36:22 +00:00
if (!$anonymousUser) {
$isActive = $db->getValue(
'SELECT active FROM account.user WHERE `name` = #',
[$user]
);
if (!$isActive)
throw new UserDisabledException();
}
$db->query('CALL account.myUser_loginWithName(#)', [$user]);
2019-07-02 11:43:52 +00:00
$userChanged = !$anonymousUser
2018-05-23 10:14:20 +00:00
&&(empty($_SESSION['user']) || $_SESSION['user'] != $user);
2016-09-20 18:36:22 +00:00
$_SESSION['user'] = $user;
// Registering the user access
2018-05-23 10:14:20 +00:00
if (isset($_SESSION['access']) && $userChanged)
$db->query(
2019-05-21 14:16:27 +00:00
'CALL visitUser_new(#, #)',
2018-05-23 10:14:20 +00:00
[$_SESSION['access'], session_id()]
2016-09-20 18:36:22 +00:00
);
}
/**
* Logouts the current user. Cleans the last saved used credentials.
*/
2018-05-23 10:14:20 +00:00
function logout() {
2022-05-05 13:56:17 +00:00
if (!empty($_SERVER['HTTP_AUTHORIZATION']))
$db->query(
'DELETE FROM salix.AccessToken WHERE id = #',
[$_SERVER['HTTP_AUTHORIZATION']]
);
2018-05-23 10:14:20 +00:00
unset($_SESSION['user']);
2016-09-20 18:36:22 +00:00
}
2022-05-05 13:56:17 +00:00
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
*/
2018-05-23 10:14:20 +00:00
function getUserDb($user) {
2016-09-23 22:47:34 +00:00
if ($this->userDb)
return $this->userDb;
2018-05-23 10:14:20 +00:00
$row = $this->db->getObject(
2022-02-10 11:37:12 +00:00
'SELECT r.name, rc.mysqlPassword, rc.rolePrefix, 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]
);
2022-02-10 11:37:12 +00:00
$userName = "{$row->rolePrefix}{$row->name}";
2018-05-23 10:14:20 +00:00
$password = base64_decode($row->mysqlPassword);
$userDb = $this->app->createConnection($userName, $password, TRUE);
$userDb->query('CALL account.myUser_loginWithKey(#, #)', [$user, $row->loginKey]);
return $userDb;
2016-09-23 22:47:34 +00:00
}
2016-10-04 15:27:49 +00:00
2022-05-09 13:52:29 +00:00
/**
* Generates a authentication token for the specified $user.
*
* @param {string} $user The user name
* @param {boolean} $remember Wether to create long live token
* @return {string} The generated token
*/
function createToken($user, $remember = FALSE) {
if ($remember)
$tokenLife = 2 * WEEK;
else
$tokenLife = 30 * MIN;
$token = uid(DEFAULT_TOKEN_LEN);
$userId = $this->db->getValue(
'SELECT id FROM account.user WHERE `name` = #',
[$user]
);
$this->db->query(
'INSERT INTO salix.AccessToken
SET id = #,
ttl = #,
created = NOW(),
userId = #',
[$token, $tokenLife, $userId]
);
return $token;
}
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
* @return {string} The JWT generated token
*/
2022-05-09 13:52:29 +00:00
function createJwtToken($user, $remember = FALSE) {
2016-10-04 15:27:49 +00:00
if ($remember)
$tokenLife = WEEK;
else
$tokenLife = 30 * MIN;
$payload = [
'sub' => $user,
2018-05-23 10:14:20 +00:00
'exp' => time() + $tokenLife
2016-10-04 15:27:49 +00:00
];
2018-05-23 10:14:20 +00:00
$key = $this->db->getValue('SELECT jwtKey FROM config');
return Jwt::encode($payload, $key);
2016-10-04 15:27:49 +00:00
}
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
*/
2018-05-23 10:14:20 +00:00
function getVersion() {
$appName = $this->app->getName();
$version = apcu_fetch("$appName.version", $success);
2016-09-23 22:47:34 +00:00
2018-05-23 10:14:20 +00:00
if (!$success) {
if (file_exists('package.json')) {
$package = json_decode(file_get_contents('package.json'));
$version = new Version($package->version);
2018-05-23 11:09:55 +00:00
} else
$version = new Version();
2016-09-23 22:47:34 +00:00
apcu_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.
*/
2018-05-23 10:14:20 +00:00
function checkVersion() {
if (empty($_COOKIE['vnVersion'])) return;
$client = new Version($_COOKIE['vnVersion']);
$last = $this->getVersion();
2016-09-23 22:47:34 +00:00
if ($client->isLowerThan($last))
2018-05-23 10:14:20 +00:00
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
*/
2018-05-23 10:14:20 +00:00
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
*/
2018-05-23 10:14:20 +00:00
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
*/
2018-05-23 10:14:20 +00:00
function getUrl() {
$proto = $this->isHttps() ? 'https' : 'http';
return "$proto://{$this->getUri()}";
2016-09-24 14:32:31 +00:00
}
}