- fixed some "potential" login issues, #718

- improved error logging in case of failed login attempts
- improved ESI "access token" handling
This commit is contained in:
Mark Friedrich
2018-12-22 11:43:46 +01:00
parent 668ec379e6
commit 50f630f7c2
10 changed files with 237 additions and 181 deletions

View File

@@ -26,7 +26,7 @@ class AccessController extends Controller {
if($return = parent::beforeroute($f3, $params)){
// Any route/endpoint of a child class of this one,
// requires a valid logged in user!
if( !$this->isLoggedIn($f3) ){
if($this->isLoggedIn($f3) !== 'OK'){
// no character found or login timer expired
$this->logoutCharacter($f3);
// skip route handler and afterroute()
@@ -40,51 +40,40 @@ class AccessController extends Controller {
/**
* get current character and check if it is a valid character
* @param \Base $f3
* @return bool
* @return string
* @throws \Exception
*/
protected function isLoggedIn(\Base $f3): bool {
$loginCheck = false;
if( $character = $this->getCharacter() ){
if($this->checkLogTimer($f3, $character)){
if($character->isAuthorized() === 'OK'){
$loginCheck = true;
protected function isLoggedIn(\Base $f3): string {
$loginStatus = 'UNKNOWN';
if($character = $this->getCharacter()){
if($character->checkLoginTimer()){
if(( $authStatus = $character->isAuthorized()) === 'OK'){
$loginStatus = 'OK';
}else{
$loginStatus = $authStatus;
}
}else{
$loginStatus = 'MAX LOGIN TIME EXCEEDED';
}
}else{
$loginStatus = 'NO SESSION FOUND';
}
return $loginCheck;
}
/**
* checks whether a user/character is currently logged in
* @param \Base $f3
* @param Model\CharacterModel $character
* @return bool
*/
private function checkLogTimer(\Base $f3, Model\CharacterModel $character){
$loginCheck = false;
// log character access status in debug mode
if(
!$character->dry() &&
$character->lastLogin
$loginStatus !== 'OK' &&
$f3->get('DEBUG') === 3
){
// check logIn time
$logInTime = new \DateTime($character->lastLogin);
$now = new \DateTime();
$timeDiff = $now->diff($logInTime);
$minutes = $timeDiff->days * 60 * 24 * 60;
$minutes += $timeDiff->h * 60;
$minutes += $timeDiff->i;
if($minutes <= Config::getPathfinderData('timer.logged')){
$loginCheck = true;
}
self::getLogger('CHARACTER_ACCESS')->write(
sprintf(Model\CharacterModel::LOG_ACCESS,
$character->_id ,
$loginStatus,
$character->name
)
);
}
return $loginCheck;
return $loginStatus;
}
/**

View File

@@ -209,6 +209,9 @@ class Map extends Controller\AccessController {
$validInitData = $validInitData ? !empty($structureData) : $validInitData;
// get available wormhole types ---------------------------------------------------------------------------
/**
* @var $wormhole Model\Universe\WormholeModel
*/
$wormhole = Model\Universe\BasicUniverseModel::getNew('WormholeModel');
$wormholesData = [];
if($rows = $wormhole->find(null, ['order' => 'name asc'])){
@@ -424,7 +427,7 @@ class Map extends Controller\AccessController {
$return->error = [];
if( isset($formData['id']) ){
$activeCharacter = $this->getCharacter(0);
$activeCharacter = $this->getCharacter();
/**
* @var $map Model\MapModel
@@ -870,7 +873,7 @@ class Map extends Controller\AccessController {
$getMapUserData = (bool)$postData['getMapUserData'];
$mapTracking = (bool)$postData['mapTracking'];
$systemData = (array)$postData['systemData'];
$activeCharacter = $this->getCharacter(0);
$activeCharacter = $this->getCharacter();
$return = (object)[];

View File

@@ -86,7 +86,7 @@ class User extends Controller\Controller{
$character->save();
// write login log --------------------------------------------------------------------
self::getLogger('LOGIN')->write(
self::getLogger('CHARACTER_LOGIN')->write(
sprintf(self::LOG_LOGGED_IN,
$user->_id,
$user->name,
@@ -260,7 +260,7 @@ class User extends Controller\Controller{
$formData = $data['formData'];
try{
if($activeCharacter = $this->getCharacter(0)){
if($activeCharacter = $this->getCharacter()){
$user = $activeCharacter->getUser();
// captcha is send -> check captcha -------------------------------------------
@@ -371,7 +371,7 @@ class User extends Controller\Controller{
!empty($data['captcha']) &&
$data['captcha'] === $captcha
){
$activeCharacter = $this->getCharacter(0);
$activeCharacter = $this->getCharacter();
$user = $activeCharacter->getUser();
if($user){

View File

@@ -24,11 +24,6 @@ class Sso extends Api\User{
*/
const SSO_TIMEOUT = 4;
/**
* @var int expire time (seconds) for an valid "accessToken"
*/
const ACCESS_KEY_EXPIRE_TIME = 20 * 60;
// SSO specific session keys
const SESSION_KEY_SSO = 'SESSION.SSO';
const SESSION_KEY_SSO_ERROR = 'SESSION.SSO.ERROR';
@@ -71,7 +66,7 @@ class Sso extends Api\User{
if(
isset($params['characterId']) &&
( $activeCharacter = $this->getCharacter(0) )
( $activeCharacter = $this->getCharacter() )
){
// authentication restricted to a characterId -----------------------------------------------
// restrict login to this characterId e.g. for character switch on map page
@@ -187,10 +182,7 @@ class Sso extends Api\User{
$accessData = $this->getSsoAccessData($getParams['code']);
if(
isset($accessData->accessToken) &&
isset($accessData->refreshToken)
){
if(isset($accessData->accessToken, $accessData->esiAccessTokenExpires, $accessData->refreshToken)){
// login succeeded -> get basic character data for current login
$verificationCharacterData = $this->verifyCharacterData($accessData->accessToken);
@@ -205,10 +197,11 @@ class Sso extends Api\User{
if( isset($characterData->character) ){
// add "ownerHash" and SSO tokens
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
$characterData->character['crestAccessToken'] = $accessData->accessToken;
$characterData->character['crestRefreshToken'] = $accessData->refreshToken;
$characterData->character['esiScopes'] = Lib\Util::convertScopesString($verificationCharacterData->Scopes);
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
$characterData->character['esiAccessToken'] = $accessData->accessToken;
$characterData->character['esiAccessTokenExpires'] = $accessData->esiAccessTokenExpires;
$characterData->character['esiRefreshToken'] = $accessData->refreshToken;
$characterData->character['esiScopes'] = Lib\Util::convertScopesString($verificationCharacterData->Scopes);
// add/update static character data
$characterModel = $this->updateCharacter($characterData);
@@ -338,7 +331,7 @@ class Sso extends Api\User{
* @param bool $authCode
* @return null|\stdClass
*/
public function getSsoAccessData($authCode){
protected function getSsoAccessData($authCode){
$accessData = null;
if( !empty($authCode) ){
@@ -354,10 +347,10 @@ class Sso extends Api\User{
/**
* verify authorization code, and get an "access_token" data
* @param $authCode
* @param string $authCode
* @return \stdClass
*/
protected function verifyAuthorizationCode($authCode){
protected function verifyAuthorizationCode(string $authCode){
$requestParams = [
'grant_type' => 'authorization_code',
'code' => $authCode
@@ -369,32 +362,35 @@ class Sso extends Api\User{
/**
* get new "access_token" by an existing "refresh_token"
* -> if "access_token" is expired, this function gets a fresh one
* @param $refreshToken
* @param string $refreshToken
* @param array $additionalOptions
* @return \stdClass
*/
public function refreshAccessToken($refreshToken){
public function refreshAccessToken(string $refreshToken, array $additionalOptions = []){
$requestParams = [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
];
return $this->requestAccessData($requestParams);
return $this->requestAccessData($requestParams, $additionalOptions);
}
/**
* request an "access_token" AND "refresh_token" data
* -> this can either be done by sending a valid "authorization code"
* OR by providing a valid "refresh_token"
* @param $requestParams
* @param array $requestParams
* @param array $additionalOptions
* @return \stdClass
*/
protected function requestAccessData($requestParams){
protected function requestAccessData(array $requestParams, array $additionalOptions = []){
$verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint();
$verifyAuthCodeUrlParts = parse_url($verifyAuthCodeUrl);
$accessData = (object) [];
$accessData->accessToken = null;
$accessData->refreshToken = null;
$accessData->esiAccessTokenExpires = 0;
if($verifyAuthCodeUrlParts){
$contentType = 'application/x-www-form-urlencoded';
@@ -412,17 +408,30 @@ class Sso extends Api\User{
// content (parameters to send with)
$requestOptions['content'] = http_build_query($requestParams);
$apiResponse = Lib\Web::instance()->request($verifyAuthCodeUrl, $requestOptions);
$apiResponse = Lib\Web::instance()->request($verifyAuthCodeUrl, $requestOptions, $additionalOptions);
if($apiResponse['body']){
$authCodeRequestData = json_decode($apiResponse['body'], true);
if( !empty($authCodeRequestData) ){
if( isset($authCodeRequestData['access_token']) ){
// this token is required for endpoints that require Auth
// accessToken is required for endpoints that require Auth
$accessData->accessToken = $authCodeRequestData['access_token'];
}
if(isset($authCodeRequestData['expires_in'])){
// expire time for accessToken
try{
$timezone = $this->getF3()->get('getTimeZone')();
$accessTokenExpires = new \DateTime('now', $timezone);
$accessTokenExpires->add(new \DateInterval('PT' . (int)$authCodeRequestData['expires_in'] . 'S'));
$accessData->esiAccessTokenExpires = $accessTokenExpires->format('Y-m-d H:i:s');
}catch(\Exception $e){
$this->getF3()->error(500, $e->getMessage(), $e->getTrace());
}
}
if(isset($authCodeRequestData['refresh_token'])){
// this token is used to refresh/get a new access_token when expires
$accessData->refreshToken = $authCodeRequestData['refresh_token'];
@@ -546,7 +555,7 @@ class Sso extends Api\User{
$character = Model\BasicModel::getNew('CharacterModel');
$character->getById((int)$characterData->character['id'], 0);
$character->copyfrom($characterData->character, [
'id', 'name', 'ownerHash', 'crestAccessToken', 'crestRefreshToken', 'esiScopes', 'securityStatus'
'id', 'name', 'ownerHash', 'esiAccessToken', 'esiAccessTokenExpires', 'esiRefreshToken', 'esiScopes', 'securityStatus'
]);
$character->corporationId = $characterData->corporation;
@@ -562,7 +571,7 @@ class Sso extends Api\User{
* -> This header is required for any Auth-required endpoints!
* @return string
*/
protected function getAuthorizationHeader(){
protected function getAuthorizationHeader() : string {
return base64_encode(
Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') . ':'
. Controller\Controller::getEnvironmentData('CCP_SSO_SECRET_KEY')
@@ -574,7 +583,7 @@ class Sso extends Api\User{
* -> throw error if url is broken/missing
* @return string
*/
static function getSsoUrlRoot(){
static function getSsoUrlRoot() : string {
$url = '';
if( \Audit::instance()->url(self::getEnvironmentData('CCP_SSO_URL')) ){
$url = self::getEnvironmentData('CCP_SSO_URL');
@@ -587,15 +596,15 @@ class Sso extends Api\User{
return $url;
}
static function getAuthorizationEndpoint(){
static function getAuthorizationEndpoint() : string {
return self::getSsoUrlRoot() . '/oauth/authorize';
}
static function getVerifyAuthorizationCodeEndpoint(){
static function getVerifyAuthorizationCodeEndpoint() : string {
return self::getSsoUrlRoot() . '/oauth/token';
}
static function getVerifyUserEndpoint(){
static function getVerifyUserEndpoint() : string {
return self::getSsoUrlRoot() . '/oauth/verify';
}
@@ -603,7 +612,7 @@ class Sso extends Api\User{
* get logger for SSO logging
* @return \Log
*/
static function getSSOLogger(){
static function getSSOLogger() : \Log {
return parent::getLogger('SSO');
}
}

View File

@@ -126,30 +126,29 @@ class Controller {
protected function initSession(\Base $f3){
$session = null;
/**
* callback() for suspect sessions
* @param $session
* @param $sid
* @return bool
*/
$onSuspect = function($session, $sid){
self::getLogger('SESSION_SUSPECT')->write( sprintf(
self::ERROR_SESSION_SUSPECT,
$sid,
$session->ip(),
$session->agent()
));
// .. continue with default onSuspect() handler
// -> destroy session
return false;
};
if(
$f3->get('SESSION_CACHE') === 'mysql' &&
$this->getDB('PF') instanceof DB\SQL
){
if(!headers_sent() && session_status()!=PHP_SESSION_ACTIVE){
/**
* callback() for suspect sessions
* @param $session
* @param $sid
* @return bool
*/
$onSuspect = function($session, $sid){
self::getLogger('SESSION_SUSPECT')->write( sprintf(
self::ERROR_SESSION_SUSPECT,
$sid,
$session->ip(),
$session->agent()
));
// .. continue with default onSuspect() handler
// -> destroy session
return false;
};
new DB\SQL\MySQL\Session($this->getDB('PF'), 'sessions', true, $onSuspect);
}
}

View File

@@ -20,6 +20,16 @@ class Config extends \Prefab {
const CACHE_KEY_SOCKET_VALID = 'CACHED_SOCKET_VALID';
const CACHE_TTL_SOCKET_VALID = 60;
/**
* SSO downtime length (estimation), minutes
*/
const DOWNTIME_LENGTH = 8;
/**
* SSO downtime buffer length extends downtime length, minutes
*/
const DOWNTIME_BUFFER = 1;
const ERROR_CONF_PATHFINDER = 'Config value missing in pathfinder.ini file [%s]';
const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. -> Check installed Composer packages';
@@ -411,10 +421,9 @@ class Config extends \Prefab {
* -> can be used for prevent logging errors during downTime
* @param \DateTime|null $dateCheck
* @return bool
* @throws Exception\DateException
* @throws \Exception
*/
static function inDownTimeRange(\DateTime $dateCheck = null) : bool {
$inRange = false;
// default daily downtime 00:00am
$downTimeParts = [0, 0];
if( !empty($downTime = (string)self::getEnvironmentData('CCP_SSO_DOWNTIME')) ){
@@ -425,19 +434,28 @@ class Config extends \Prefab {
}
}
// downTime Range is 10m
$downtimeInterval = new \DateInterval('PT10M');
$timezone = \Base::instance()->get('getTimeZone')();
try{
// downTime Range is 10m
$downtimeLength = self::DOWNTIME_LENGTH + (2 * self::DOWNTIME_BUFFER);
$timezone = \Base::instance()->get('getTimeZone')();
// if set -> use current time
$dateCheck = is_null($dateCheck) ? new \DateTime('now', $timezone) : $dateCheck;
$dateDowntimeStart = new \DateTime('now', $timezone);
$dateDowntimeStart->setTime($downTimeParts[0],$downTimeParts[1]);
$dateDowntimeEnd = clone $dateDowntimeStart;
$dateDowntimeEnd->add($downtimeInterval);
// if not set -> use current time
$dateCheck = is_null($dateCheck) ? new \DateTime('now', $timezone) : $dateCheck;
$dateDowntimeStart = new \DateTime('now', $timezone);
$dateDowntimeStart->setTime($downTimeParts[0],$downTimeParts[1]);
$dateDowntimeStart->sub(new \DateInterval('PT' . self::DOWNTIME_BUFFER . 'M'));
$dateRange = new DateRange($dateDowntimeStart, $dateDowntimeEnd);
return $dateRange->inRange($dateCheck);
$dateDowntimeEnd = clone $dateDowntimeStart;
$dateDowntimeEnd->add(new \DateInterval('PT' . $downtimeLength . 'M'));
$dateRange = new DateRange($dateDowntimeStart, $dateDowntimeEnd);
$inRange = $dateRange->inRange($dateCheck);
}catch(\Exception $e){
$f3 = \Base::instance();
$f3->error(500, $e->getMessage(), $e->getTrace());
}
return $inRange;
}
}

View File

@@ -15,10 +15,11 @@ class Web extends \Web {
const ERROR_STATUS_LOG = 'HTTP %s: \'%s\' | url: %s \'%s\'%s';
/**
* max number of curls calls for a single resource until giving up...
* this is because SSO API is not very stable
* max retry attempts (cURL requests) for a single resource until giving up...
* -> this is because SSO API is not very stable
* 2 == (initial request + 2 retry == max of 3)
*/
const RETRY_COUNT_MAX = 3;
const RETRY_COUNT_MAX = 2;
/**
* end of line
@@ -31,9 +32,8 @@ class Web extends \Web {
* @param array $headers
* @return int
*/
protected function getStatusCodeFromHeaders($headers = []){
protected function getStatusCodeFromHeaders(array $headers = []) : int {
$statusCode = 0;
if(
preg_match(
'/HTTP\/1\.\d (\d{3}?)/',
@@ -51,9 +51,8 @@ class Web extends \Web {
* @param array $headers
* @return int
*/
protected function getCacheTimeFromHeaders($headers = []){
protected function getCacheTimeFromHeaders(array $headers = []) : int {
$cacheTime = 0;
if(
preg_match(
'/Cache-Control:(.*?)max-age=([0-9]+)/',
@@ -80,10 +79,10 @@ class Web extends \Web {
* @param null $options
* @return string
*/
protected function getCacheKey($url, $options = null){
protected function getCacheKey(string $url, $options = null) : string {
$f3 = \Base::instance();
$headers = isset($options['header']) ? implode($this->eol, (array) $options['header']) : '';
$headers = isset($options['header']) ? implode($this->eol, (array)$options['header']) : '';
return $f3->hash(
$options['method'] . ' '
@@ -100,13 +99,15 @@ class Web extends \Web {
* @param array $additionalOptions
* @param int $retryCount request counter for failed call
* @return array|FALSE|mixed
* @throws \Exception\DateException
*/
public function request($url,array $options = null, $additionalOptions = [], $retryCount = 0){
public function request($url, array $options = null, array $additionalOptions = [], int $retryCount = 0){
$f3 = \Base::instance();
if( !$f3->exists( $hash = $this->getCacheKey($url, $options) ) ){
// retry same request until request limit is reached
if( !$f3->exists($hash = $this->getCacheKey($url, $options), $result) ){
// set max retry count in case of request error
$retryCountMax = isset($additionalOptions['retryCountMax']) ? (int)$additionalOptions['retryCountMax'] : self::RETRY_COUNT_MAX;
// retry same request until retry limit is reached
$retry = false;
$result = parent::request($url, $options);
@@ -148,7 +149,7 @@ class Web extends \Web {
case 505:
$retry = true;
if( $retryCount == self::RETRY_COUNT_MAX ){
if($retryCount == $retryCountMax){
$errorMsg = $this->getErrorMessageFromJsonResponse(
$statusCode,
$options['method'],
@@ -171,7 +172,7 @@ class Web extends \Web {
case 0:
$retry = true;
if( $retryCount == self::RETRY_COUNT_MAX ){
if($retryCount == $retryCountMax){
// timeout -> response should not be cached
$result['timeout'] = true;
@@ -208,14 +209,11 @@ class Web extends \Web {
if(
$retry &&
$retryCount < self::RETRY_COUNT_MAX
$retryCount < $retryCountMax
){
$retryCount++;
$this->request($url, $options, $additionalOptions, $retryCount);
}
}else{
$result = $f3->get($hash);
}
return $result;

View File

@@ -23,7 +23,9 @@ class CharacterModel extends BasicModel {
/**
* cache key prefix for getData(); result WITH log data
*/
const DATA_CACHE_KEY_LOG = 'LOG';
const DATA_CACHE_KEY_LOG = 'LOG';
const LOG_ACCESS = 'charId: [%20s], status: %s, charName: %s';
/**
* character authorization status
@@ -75,15 +77,15 @@ class CharacterModel extends BasicModel {
'nullable' => false,
'default' => ''
],
'crestAccessToken' => [
'esiAccessToken' => [
'type' => Schema::DT_VARCHAR256
],
'crestAccessTokenUpdated' => [
'esiAccessTokenExpires' => [
'type' => Schema::DT_TIMESTAMP,
'default' => Schema::DF_CURRENT_TIMESTAMP,
'index' => true
],
'crestRefreshToken' => [
'esiRefreshToken' => [
'type' => Schema::DT_VARCHAR256
],
'esiScopes' => [
@@ -193,10 +195,6 @@ class CharacterModel extends BasicModel {
$characterData->logLocation = $this->logLocation;
$characterData->selectLocation = $this->selectLocation;
if($this->authStatus){
$characterData->authStatus = $this->authStatus;
}
if($addCharacterLogData){
if($logModel = $this->getLog()){
$characterData->log = $logModel->getData();
@@ -219,6 +217,11 @@ class CharacterModel extends BasicModel {
$this->updateCacheData($characterData, $cacheKeyModifier);
}
// temp "authStatus" should not be cached
if($this->authStatus){
$characterData->authStatus = $this->authStatus;
}
return $characterData;
}
@@ -261,20 +264,6 @@ class CharacterModel extends BasicModel {
return $ownerHash;
}
/**
* set API accessToken for current session
* -> update "tokenUpdated" column on change
* -> this is required for expire checking!
* @param string $accessToken
* @return string
*/
public function set_crestAccessToken($accessToken){
if($this->crestAccessToken !== $accessToken){
$this->touch('crestAccessTokenUpdated');
}
return $accessToken;
}
/**
* setter for "kicked" until time
* @param $minutes
@@ -484,47 +473,61 @@ class CharacterModel extends BasicModel {
*/
public function getAccessToken(){
$accessToken = false;
$refreshToken = true;
$timezone = self::getF3()->get('getTimeZone')();
$now = new \DateTime('now', $timezone);
// check if there is already an "accessToken" for this user
// check expire timer for stored "accessToken"
if(
!empty($this->crestAccessToken) &&
!empty($this->crestAccessTokenUpdated)
!empty($this->esiAccessToken) &&
!empty($this->esiAccessTokenExpires)
){
$timezone = self::getF3()->get('getTimeZone')();
$tokenTime = \DateTime::createFromFormat(
$expireTime = \DateTime::createFromFormat(
'Y-m-d H:i:s',
$this->crestAccessTokenUpdated,
$this->esiAccessTokenExpires,
$timezone
);
// add expire time buffer for this "accessToken"
// token should be marked as "deprecated" BEFORE it actually expires.
$timeBuffer = 2 * 60;
$tokenTime->add(new \DateInterval('PT' . (Sso::ACCESS_KEY_EXPIRE_TIME - $timeBuffer) . 'S'));
$now = new \DateTime('now', $timezone);
if($tokenTime->getTimestamp() > $now->getTimestamp()){
$accessToken = $this->crestAccessToken;
// check if token is not expired
if($expireTime->getTimestamp() > $now->getTimestamp()){
// token still valid
$accessToken = $this->esiAccessToken;
// check if token should be renewed (close to expire)
$timeBuffer = 2 * 60;
$expireTime->sub(new \DateInterval('PT' . $timeBuffer . 'S'));
if($expireTime->getTimestamp() > $now->getTimestamp()){
// token NOT close to expire
$refreshToken = false;
}
}
}
// if no "accessToken" was found -> get a fresh one by an existing "refreshToken"
// no valid "accessToken" found OR
// existing token is close to expire
// -> get a fresh one by an existing "refreshToken"
// -> in case request for new token fails (e.g. timeout) and old token is still valid -> keep old token
if(
!$accessToken &&
!empty($this->crestRefreshToken)
$refreshToken &&
!empty($this->esiRefreshToken)
){
// no accessToken found OR token is deprecated
$ssoController = new Sso();
$accessData = $ssoController->refreshAccessToken($this->crestRefreshToken);
$additionalOptions = [];
if($accessToken){
// ... close to expire token exists -> moderate failover settings
$additionalOptions['suppressHTTPErrors'] = true;
$additionalOptions['retryCountMax'] = 0;
}
if(
isset($accessData->accessToken) &&
isset($accessData->refreshToken)
){
$this->crestAccessToken = $accessData->accessToken;
$ssoController = new Sso();
$accessData = $ssoController->refreshAccessToken($this->esiRefreshToken, $additionalOptions);
if(isset($accessData->accessToken, $accessData->esiAccessTokenExpires, $accessData->refreshToken)){
$this->esiAccessToken = $accessData->accessToken;
$this->esiAccessTokenExpires = $accessData->esiAccessTokenExpires;
$this->save();
$accessToken = $this->crestAccessToken;
$accessToken = $this->esiAccessToken;
}
}
@@ -535,18 +538,53 @@ class CharacterModel extends BasicModel {
* check if character is currently kicked
* @return bool
*/
public function isKicked(){
public function isKicked() : bool {
$kicked = false;
if( !is_null($this->kicked) ){
$kickedUntil = new \DateTime();
$kickedUntil->setTimestamp( (int)strtotime($this->kicked) );
$now = new \DateTime();
$kicked = ($kickedUntil > $now);
try{
$kickedUntil = new \DateTime();
$kickedUntil->setTimestamp( (int)strtotime($this->kicked) );
$now = new \DateTime();
$kicked = ($kickedUntil > $now);
}catch(\Exception $e){
self::getF3()->error(500, $e->getMessage(), $e->getTrace());
}
}
return $kicked;
}
/**
* checks whether this character is currently logged in
* @return bool
*/
public function checkLoginTimer() : bool {
$loginCheck = false;
if( !$this->dry() && $this->lastLogin ){
// get max login time (minutes) from config
$maxLoginMinutes = (int)Config::getPathfinderData('timer.logged');
if($maxLoginMinutes){
$timezone = self::getF3()->get('getTimeZone')();
try{
$now = new \DateTime('now', $timezone);
$logoutTime = new \DateTime($this->lastLogin, $timezone);
$logoutTime->add(new \DateInterval('PT' . $maxLoginMinutes . 'M'));
if($logoutTime->getTimestamp() > $now->getTimestamp()){
$loginCheck = true;
}
}catch(\Exception $e){
self::getF3()->error(500, $e->getMessage(), $e->getTrace());
}
}else{
// no "max login" timer configured -> character still logged in
$loginCheck = true;
}
}
return $loginCheck;
}
/**
* checks whether this character is authorized to log in
* -> check corp/ally whitelist config (pathfinder.ini)
@@ -1111,7 +1149,7 @@ class CharacterModel extends BasicModel {
}
// delete auth cookie data ------------------------------------------------------------------------------------
if($deleteCookie ){
if($deleteCookie){
$this->deleteAuthentications();
}
}

View File

@@ -189,13 +189,15 @@ EXPIRE_SIGNATURES = 259200
ERROR = error
; SSO error log
SSO = sso
; login information
LOGIN = login
; login info
CHARACTER_LOGIN = character_login
; character access info
CHARACTER_ACCESS = character_access
; session warnings (suspect)
SESSION_SUSPECT = session_suspect
; account deleted
DELETE_ACCOUNT = account_delete
; admin action (e.g. kick, bann) log
; admin action (e.g. kick, ban) log
ADMIN = admin
; TCP socket errors
SOCKET_ERROR = socket_error

View File

@@ -1,4 +1,4 @@
<div id="{{id}}" class="alert alert-warning">
<div class="ui-pnotify-icon"><span class="fas fa-exclamation fa-fw fa-lg"></span></div>
<h4 class="ui-pnotify-title">Scheduled maintenance: Update v1.4.2 <i class="fas fa-long-arrow-alt-right"></i> v1.4.3; Shutdown: ~19:00 (UTC). ETA ~19:20 (UTC)</h4>
<h4 class="ui-pnotify-title">Scheduled maintenance: Database upgrade <em>MariaDB</em> v10.1 <i class="fas fa-long-arrow-alt-right"></i> v10.3; Shutdown: ~14:00 (UTC). ETA ~14:45 (UTC)</h4>
</div>