app = $app; } function init() { $this->db = $this->app->getSysConn(); } /** * Starts the user session. */ function startSession() { $db = $this->app->getSysConn(); ini_set('session.cookie_secure', $this->isHttps()); ini_set('session.hash_function', 'sha256'); session_set_save_handler(new DbSessionHandler($db)); session_start(); // Setting the locale if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) if (!isset($_SESSION['httpLanguage']) || $_SESSION['httpLanguage'] != $_SERVER['HTTP_ACCEPT_LANGUAGE']) { $_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) if (TRUE || stream_resolve_include_path("locale/$lang")) { $_SESSION['lang'] = $lang; break; } } if (!isset($_SESSION['lang'])) $_SESSION['lang'] = NULL; Locale::set($_SESSION['lang']); Locale::addPath('vn/web'); // Registering the visit if (isset($_COOKIE['PHPSESSID']) || isset($_SESSION['access']) || isset($_SESSION['skipVisit']) || !isset($_SERVER['HTTP_USER_AGENT'])) return; $agent = $_SERVER['HTTP_USER_AGENT']; $browser = get_browser($agent, TRUE); if (!empty($browser['crawler'])) { $_SESSION['skipVisit'] = TRUE; return; } if (isset($_SERVER['REMOTE_ADDR'])) $ip = ip2long($_SERVER['REMOTE_ADDR']); $row = $db->getRow( 'CALL visit_register(#, #, #, #, #, #, #, #, #)', [ 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'])) { $domain = explode(':', $_SERVER['HTTP_HOST'])[0]; setcookie('vnVisit', $row['visit'], [ 'expires' => time() + 31536000, // 1 Year 'samesite' => 'Lax', 'domain' => $domain ]); $_SESSION['access'] = $row['access']; } else $_SESSION['skipVisit'] = TRUE; } /** * Authenticates the user with it's credentials or token. * * return Db\Conn The database connection */ function login() { $db = $this->db; $anonymousUser = TRUE; $token = NULL; if (!empty($_SERVER['HTTP_AUTHORIZATION'])) $token = $_SERVER['HTTP_AUTHORIZATION']; if (!empty($_GET['access_token'])) $token = $_GET['access_token']; if (isset($token)) { $userId = $db->getValue( 'SELECT userId FROM salix.AccessToken WHERE id = # AND NOW() <= TIMESTAMPADD(SECOND, ttl, created)', [$token] ); if (!$userId) throw new SessionExpiredException(); $anonymousUser = FALSE; $user = $db->getValue( 'SELECT `name` FROM account.user WHERE id = #', [$userId] ); } else $user = $db->getValue('SELECT guestUser FROM config'); 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]); $userChanged = !$anonymousUser &&(empty($_SESSION['user']) || $_SESSION['user'] != $user); $_SESSION['user'] = $user; // Registering the user access if (isset($_SESSION['access']) && $userChanged) $db->query( 'CALL visitUser_new(#, #)', [$_SESSION['access'], session_id()] ); } /** * Logouts the current user. Cleans the last saved used credentials. */ function logout() { if (!empty($_SERVER['HTTP_AUTHORIZATION'])) $db->query( 'DELETE FROM salix.AccessToken WHERE id = #', [$_SERVER['HTTP_AUTHORIZATION']] ); unset($_SESSION['user']); } /** * Creates or returns a database connection where the authenticated user * is the role of the current logged user. * * @return {Db\Conn} The database connection */ function getUserDb($user) { if ($this->userDb) return $this->userDb; $row = $this->db->getObject( '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] ); $userName = "{$row->rolePrefix}{$row->name}"; $password = base64_decode($row->mysqlPassword); $userDb = $this->app->createConnection($userName, $password, TRUE); $userDb->query('CALL account.myUser_loginWithKey(#, #)', [$user, $row->loginKey]); return $userDb; } /** * 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; } /** * 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 */ function createJwtToken($user, $remember = FALSE) { if ($remember) $tokenLife = WEEK; else $tokenLife = 30 * MIN; $payload = [ 'sub' => $user, 'exp' => time() + $tokenLife ]; $key = $this->db->getValue('SELECT jwtKey FROM config'); return Jwt::encode($payload, $key); } /** * Obtains the application version number. It is extracted and * cached from package.json file. * * @return string The version number */ function getVersion() { $appName = $this->app->getName(); $version = apcu_fetch("$appName.version", $success); if (!$success) { if (file_exists('package.json')) { $package = json_decode(file_get_contents('package.json')); $version = new Version($package->version); } else $version = new Version(); apcu_store("$appName.version", $version); } return $version; } /** * Checks the client version. */ function checkVersion() { if (empty($_COOKIE['vnVersion'])) return; $client = new Version($_COOKIE['vnVersion']); $last = $this->getVersion(); if ($client->isLowerThan($last)) throw new OutdatedVersionException(); } /** * 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. * * @return string The current URI */ function getUri() { return "{$_SERVER['SERVER_NAME']}{$_SERVER['REQUEST_URI']}"; } /** * Returns the current URL without the GET part. * * @return string The current URL */ function getUrl() { $proto = $this->isHttps() ? 'https' : 'http'; return "$proto://{$this->getUri()}"; } }