diff --git a/app/config/collections.php b/app/config/collections.php index 237ed3e74f..de11c5ccc3 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -337,6 +337,123 @@ $collections = [ 'required' => true, 'array' => false, ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'OS Code', + 'key' => 'osCode', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'OS Name', + 'key' => 'osName', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'OS Version', + 'key' => 'osVersion', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Type', + 'key' => 'clientType', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Code', + 'key' => 'clientCode', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Name', + 'key' => 'clientName', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Version', + 'key' => 'clientVersion', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Engine', + 'key' => 'clientEngine', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Client Engine Version', + 'key' => 'clientEngineVersion', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Device Name', + 'key' => 'deviceName', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Device Brand', + 'key' => 'deviceBrand', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Device Model', + 'key' => 'deviceModel', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], + [ + '$collection' => Database::SYSTEM_COLLECTION_RULES, + 'label' => 'Country Code', + 'key' => 'countryCode', + 'type' => Database::SYSTEM_VAR_TYPE_TEXT, + 'default' => '', + 'required' => false, + 'array' => false, + ], ], ], Database::SYSTEM_COLLECTION_MEMBERSHIPS => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 9c1aedbd3c..d51e2e4ba7 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -141,10 +141,12 @@ App::post('/v1/account/sessions') ->label('abuse-key', 'url:{url},email:{param-email}') ->param('email', '', function () { return new Email(); }, 'User email.') ->param('password', '', function () { return new Password(); }, 'User password. Must be between 6 to 32 chars.') - ->action(function ($email, $password, $request, $response, $projectDB, $webhooks, $audits) { + ->action(function ($email, $password, $request, $response, $projectDB, $locale, $geodb, $webhooks, $audits) { /** @var Appwrite\Swoole\Request $request */ /** @var Appwrite\Swoole\Response $response */ /** @var Appwrite\Database\Database $projectDB */ + /** @var Utopia\Locale\Locale $locale */ + /** @var GeoIp2\Database\Reader $geodb */ /** @var Appwrite\Event\Event $webhooks */ /** @var Appwrite\Event\Event $audits */ @@ -167,6 +169,23 @@ App::post('/v1/account/sessions') throw new Exception('Invalid credentials', 401); // Wrong password or username } + $dd = new DeviceDetector($request->getUserAgent('UNKNOWN')); + + $dd->parse(); + + $os = $dd->getOs(); + $osCode = (isset($os['short_name'])) ? $os['short_name'] : ''; + $osName = (isset($os['name'])) ? $os['name'] : ''; + $osVersion = (isset($os['version'])) ? $os['version'] : ''; + + $client = $dd->getClient(); + $clientType = (isset($client['type'])) ? $client['type'] : ''; + $clientCode = (isset($client['short_name'])) ? $client['short_name'] : ''; + $clientName = (isset($client['name'])) ? $client['name'] : ''; + $clientVersion = (isset($client['version'])) ? $client['version'] : ''; + $clientEngine = (isset($client['engine'])) ? $client['engine'] : ''; + $clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : ''; + $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $secret = Auth::tokenGenerator(); $session = new Document([ @@ -177,8 +196,32 @@ App::post('/v1/account/sessions') 'expire' => $expiry, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), + + 'osCode' => $osCode, + 'osName' => $osName, + 'osVersion' => $osVersion, + 'clientType' => $clientType, + 'clientCode' => $clientCode, + 'clientName' => $clientName, + 'clientVersion' => $clientVersion, + 'clientEngine' => $clientEngine, + 'clientEngineVersion' => $clientEngineVersion, + 'deviceName' => $dd->getDeviceName(), + 'deviceBrand' => $dd->getBrandName(), + 'deviceModel' => $dd->getModel(), ]); + try { + $record = $geodb->country($request->getIP()); + $session + ->setAttribute('countryCode', \strtolower($record->country->isoCode)) + ; + } catch (\Exception $e) { + $session + ->setAttribute('countryCode', '--') + ; + } + Authorization::setRole('user:'.$profile->getId()); $session = $projectDB->createDocument($session->getArrayCopy()); @@ -219,9 +262,14 @@ App::post('/v1/account/sessions') ->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; + + $session + ->setAttribute('current', true) + ->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown')) + ; $response->dynamic($session, Response::MODEL_SESSION); - }, ['request', 'response', 'projectDB', 'webhooks', 'audits']); + }, ['request', 'response', 'projectDB', 'locale', 'geodb', 'webhooks', 'audits']); App::get('/v1/account/sessions/oauth2/:provider') ->desc('Create Account Session with OAuth2') @@ -569,58 +617,34 @@ App::get('/v1/account/sessions') ->label('sdk.namespace', 'account') ->label('sdk.method', 'getSessions') ->label('sdk.description', '/docs/references/account/get-sessions.md') - ->action(function ($response, $user, $locale, $geodb) { + ->action(function ($response, $user, $locale) { /** @var Appwrite\Swoole\Response $response */ /** @var Appwrite\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ - /** @var GeoIp2\Database\Reader $geodb */ $tokens = $user->getAttribute('tokens', []); $sessions = []; - $current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret); - $index = 0; $countries = $locale->getText('countries'); + $current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret); foreach ($tokens as $token) { /* @var $token Document */ if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) { continue; } - $userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN'; + $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) + ? $countries[$token->getAttribute('contryCode')] + : $locale->getText('locale.country.unknown')); + $token->setAttribute('current', ($current == $token->getId()) ? true : false); - $dd = new DeviceDetector($userAgent); - - // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - // $dd->skipBotDetection(); - - $dd->parse(); - - $sessions[$index] = [ - '$id' => $token->getId(), - 'OS' => $dd->getOs(), - 'client' => $dd->getClient(), - 'device' => $dd->getDevice(), - 'brand' => $dd->getBrand(), - 'model' => $dd->getModel(), - 'ip' => $token->getAttribute('ip', ''), - 'geo' => [], - 'current' => ($current == $token->getId()) ? true : false, - ]; - - try { - $record = $geodb->country($token->getAttribute('ip', '')); - $sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode); - $sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown'); - } catch (\Exception $e) { - $sessions[$index]['geo']['isoCode'] = '--'; - $sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown'); - } - - ++$index; + $sessions[] = $token; } - $response->json($sessions); - }, ['response', 'user', 'locale', 'geodb']); + $response->dynamic(new Document([ + 'sum' => count($sessions), + 'sessions' => $sessions + ]), Response::MODEL_SESSION_LIST); + }, ['response', 'user', 'locale']); App::get('/v1/account/logs') ->desc('Get Account Logs') diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 5563d817de..e6cac3c764 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -158,11 +158,10 @@ App::get('/v1/users/:userId/sessions') ->label('sdk.method', 'getSessions') ->label('sdk.description', '/docs/references/users/get-user-sessions.md') ->param('userId', '', function () { return new UID(); }, 'User unique ID.') - ->action(function ($userId, $response, $projectDB, $locale, $geodb) { + ->action(function ($userId, $response, $projectDB, $locale) { /** @var Appwrite\Swoole\Response $response */ /** @var Appwrite\Database\Database $projectDB */ /** @var Utopia\Locale\Locale $locale */ - /** @var GeoIp2\Database\Reader $geodb */ $user = $projectDB->getDocument($userId); @@ -172,7 +171,6 @@ App::get('/v1/users/:userId/sessions') $tokens = $user->getAttribute('tokens', []); $sessions = []; - $index = 0; $countries = $locale->getText('countries'); foreach ($tokens as $token) { /* @var $token Document */ @@ -180,40 +178,19 @@ App::get('/v1/users/:userId/sessions') continue; } - $userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN'; + $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) + ? $countries[$token->getAttribute('contryCode')] + : $locale->getText('locale.country.unknown')); + $token->setAttribute('current', false); - $dd = new DeviceDetector($userAgent); - - // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - // $dd->skipBotDetection(); - - $dd->parse(); - - $sessions[$index] = [ - '$id' => $token->getId(), - 'OS' => $dd->getOs(), - 'client' => $dd->getClient(), - 'device' => $dd->getDevice(), - 'brand' => $dd->getBrand(), - 'model' => $dd->getModel(), - 'ip' => $token->getAttribute('ip', ''), - 'geo' => [], - ]; - - try { - $record = $geodb->country($token->getAttribute('ip', '')); - $sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode); - $sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown'); - } catch (\Exception $e) { - $sessions[$index]['geo']['isoCode'] = '--'; - $sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown'); - } - - ++$index; + $sessions[] = $token; } - $response->json($sessions); - }, ['response', 'projectDB', 'locale', 'geodb']); + $response->dynamic(new Document([ + 'sum' => count($sessions), + 'sessions' => $sessions + ]), Response::MODEL_SESSION_LIST); + }, ['response', 'projectDB', 'locale']); App::get('/v1/users/:userId/logs') ->desc('Get User Logs') diff --git a/app/views/console/account/index.phtml b/app/views/console/account/index.phtml index c8c86f729f..50e5d67c30 100644 --- a/app/views/console/account/index.phtml +++ b/app/views/console/account/index.phtml @@ -192,7 +192,7 @@ data-name="sessions" data-event="load,account.deleteRemoteSession"> -