app = $app; $this->db = $app->getSysConn (); } /** * Starts the user session. **/ function startSession () { $db = $this->db; 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'])) 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 visitRegister (#, #, #, #, #, #, #, #, #)', [ 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'])) { setcookie ('vnVisit', $row['visit'], time () + 31536000); // 1 Year $_SESSION['access'] = $row['access']; } else $_SESSION['skipVisit'] = TRUE; } /** * 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 **/ function login () { $db = $this->db; $anonymousUser = FALSE; if (isset ($_POST['user']) && isset ($_POST['password'])) { $user = strtolower ($_POST['user']); try { $db->query ('CALL account.userLogin (#, #)', [$user, $_POST['password']]); } catch (\Vn\Db\Exception $e) { if ($e->getMessage () == 'INVALID_CREDENTIALS') throw new BadLoginException (); else throw $e; } } else { if (isset ($_POST['token']) || isset ($_GET['token'])) { if (isset ($_POST['token'])) $token = $_POST['token']; if (isset ($_GET['token'])) $token = $_GET['token']; $key = $db->getValue ('SELECT jwtKey FROM config'); try { $jwtPayload = Jwt::decode ($token, $key); } catch (\Exception $e) { throw new BadLoginException ($e->getMessage ()); } $expiration = $jwtPayload['exp']; if (empty ($expiration) || $expiration <= time()) throw new SessionExpiredException (); $user = $jwtPayload['sub']; if (!empty ($jwtPayload['recover'])) $db->query ( 'UPDATE account.user SET recoverPass = TRUE WHERE name = #', [$user] ); } else { $user = $db->getValue ('SELECT guest_user FROM config'); $anonymousUser = TRUE; } $db->query ('CALL account.userLoginWithName (#)', [$user]); } $userChanged = !$anonymousUser && (empty ($_SESSION['user']) || $_SESSION['user'] != $user); $_SESSION['user'] = $user; // Registering the user access if (isset ($_SESSION['access']) && $userChanged) $db->query ( 'CALL visitUserNew (#, #)', [$_SESSION['access'], session_id ()] ); } /** * Logouts the current user. Cleans the last saved used credentials. **/ function logout () { unset ($_SESSION['user']); } /** * Creates or returns a database connection where the authenticated user * is the current logged user. * * @return {Db\Conn} The database connection **/ function getUserDb ($user) { if ($this->userDb) return $this->userDb; $password = $this->db->getValue ( 'SELECT password FROM account.user WHERE name = #', [$user]); return $this->userDb = $this->app->createConnection ($user, $password); } /** * Generates a JWT authentication token for the specified $user. * * @param {string} $user The user name * @param {boolean} $remember Wether to create long live token * @param {boolean} $recover Wether to enable recovery mode on login * @return {string} The JWT generated token **/ function createToken ($user, $remember = FALSE, $recover = FALSE) { if ($remember) $tokenLife = WEEK; else $tokenLife = 30 * MIN; $payload = [ 'sub' => $user, 'exp' => time () + $tokenLife ]; if ($recover) $payload['recover'] = 'TRUE'; $key = $this->db->getValue ('SELECT jwtKey FROM config'); return Jwt::encode ($payload, $key); } /** * Runs a method. **/ function loadMethod ($class) { $db = $this->db; $this->login (); $method = $this->app->loadMethod ( $_REQUEST['method'], $class, './rest'); $method->service = $this; if ($method::SECURITY == Security::DEFINER) { $isAuthorized = $db->getValue ('SELECT userCheckRestPriv (#)', [$_REQUEST['method']]); if (!$isAuthorized) throw new UserException (s('You don\'t have enough privileges')); $methodDb = $db; } else $methodDb = $this->getUserDb ($_SESSION['user']); if ($method::PARAMS !== NULL && !$method->checkParams ($_REQUEST, $method::PARAMS)) throw new UserException (s('Missing parameters')); Locale::addPath ("rest/{$_REQUEST['method']}"); $res = $method->run ($methodDb); $db->query ('CALL account.userLogout ()'); return $res; } /** * 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 URL without the GET part. * * @return string The current URL **/ function getUrl () { $proto = $this->isHttps () ? 'https' : 'http'; return "$proto://{$_SERVER['SERVER_NAME']}{$_SERVER['REQUEST_URI']}"; } /** * Obtains the application version number. It is based on de last * modification date of the main script. * * @return string The version number **/ function getVersion () { return (int) strftime ('%G%m%d%H%M%S', filectime ($_SERVER['SCRIPT_FILENAME'])); } /** * Obtains the relative path to document root from an absolute path. * * @return string The relative path **/ function getDir ($absoluteDir) { error_log ("Absolute: $absoluteDir"); error_log ("Root: {$_SERVER['DOCUMENT_ROOT']}"); error_log ("Script: {$_SERVER['SCRIPT_FILENAME']}"); error_log ("Self: {$_SERVER['PHP_SELF']}"); $rootLen = strlen ($_SERVER['DOCUMENT_ROOT']); return substr ($absoluteDir, $rootLen); } }