Merge remote-tracking branch 'origin/1.2.x' into feat-graphql-support

This commit is contained in:
Jake Barnby
2022-12-13 21:37:21 +13:00
6 changed files with 203 additions and 6 deletions
+5 -5
View File
@@ -140,7 +140,7 @@ App::post('/v1/account')
App::post('/v1/account/sessions/email')
->alias('/v1/account/sessions')
->desc('Create Email Session')
->groups(['api', 'account', 'auth'])
->groups(['api', 'account', 'auth', 'session'])
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
->label('auth.type', 'emailPassword')
@@ -365,7 +365,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 Redirect')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
@@ -739,7 +739,7 @@ App::post('/v1/account/sessions/magic-url')
App::put('/v1/account/sessions/magic-url')
->desc('Create Magic URL session (confirmation)')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('scope', 'public')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('audits.event', 'session.update')
@@ -981,7 +981,7 @@ App::post('/v1/account/sessions/phone')
App::put('/v1/account/sessions/phone')
->desc('Create Phone Session (confirmation)')
->groups(['api', 'account'])
->groups(['api', 'account', 'session'])
->label('scope', 'public')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('usage.metric', 'sessions.{scope}.requests.create')
@@ -1096,7 +1096,7 @@ App::put('/v1/account/sessions/phone')
App::post('/v1/account/sessions/anonymous')
->desc('Create Anonymous Session')
->groups(['api', 'account', 'auth'])
->groups(['api', 'account', 'auth', 'session'])
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
->label('auth.type', 'anonymous')
+32 -1
View File
@@ -81,7 +81,7 @@ App::post('/v1/projects')
}
$auth = Config::getParam('auth', []);
$auths = ['limit' => 0, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG];
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG];
foreach ($auth as $index => $method) {
$auths[$method['key'] ?? ''] = true;
}
@@ -576,6 +576,37 @@ App::patch('/v1/projects/:projectId/auth/:method')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/max-sessions')
->desc('Update Project users limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthLimit')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-' . APP_LIMIT_USER_SESSIONS_MAX . '. Default is ' . APP_LIMIT_USER_SESSIONS_DEFAULT)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['maxSessions'] = $limit;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::delete('/v1/projects/:projectId')
->desc('Delete Project')
->groups(['api', 'projects'])
+39
View File
@@ -318,6 +318,45 @@ App::init()
}
});
/**
* Limit user session
*
* Delete older sessions if the number of sessions have crossed
* the session limit set for the project
*/
App::shutdown()
->groups(['session'])
->inject('utopia')
->inject('request')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
$sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT;
$session = $response->getPayload();
$userId = $session['userId'] ?? '';
if (empty($userId)) {
return;
}
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
return;
}
$sessions = $user->getAttribute('sessions', []);
$count = \count($sessions);
if ($count <= $sessionLimit) {
return;
}
for ($i = 0; $i < ($count - $sessionLimit); $i++) {
$session = array_shift($sessions);
$dbForProject->deleteDocument('sessions', $session->getId());
}
$dbForProject->deleteCachedDocument('users', $userId);
});
App::shutdown()
->groups(['api'])
->inject('utopia')
+2
View File
@@ -86,6 +86,8 @@ const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_LIMIT_COUNT = 5000;
const APP_LIMIT_USERS = 10000;
const APP_LIMIT_USER_SESSIONS_MAX = 100;
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
const APP_LIMIT_ANTIVIRUS = 20000000; //20MB
const APP_LIMIT_ENCRYPTION = 20000000; //20MB
const APP_LIMIT_COMPRESSION = 20000000; //20MB
@@ -114,6 +114,12 @@ class Project extends Model
'default' => 0,
'example' => 100,
])
->addRule('authSessionsLimit', [
'type' => self::TYPE_INTEGER,
'description' => 'Max sessions allowed per user. 100 maximum.',
'default' => 10,
'example' => 10,
])
->addRule('providers', [
'type' => Response::MODEL_PROVIDER,
'description' => 'List of Providers.',
@@ -233,6 +239,7 @@ class Project extends Model
$document->setAttribute('authLimit', $authValues['limit'] ?? 0);
$document->setAttribute('authDuration', $authValues['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG);
$document->setAttribute('authSessionLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT);
foreach ($auth as $index => $method) {
$key = $method['key'];
@@ -874,6 +874,124 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
/**
* @depends testUpdateProjectAuthLimit
*/
public function testUpdateProjectAuthSessionLimit($data): array
{
$id = $data['projectId'] ?? '';
/**
* Test for failure
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/max-sessions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 0,
]);
$this->assertEquals(400, $response['headers']['status-code']);
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/max-sessions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertNotEmpty($response['body']['$id']);
$email = uniqid() . 'user@localhost.test';
$password = 'password';
$name = 'User Name';
/**
* Create new user
*/
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'userId' => ID::unique(),
'email' => $email,
'password' => $password,
'name' => $name,
]);
$this->assertEquals(201, $response['headers']['status-code']);
/**
* create new session
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $email,
'password' => $password,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$sessionId1 = $response['body']['$id'];
/**
* create new session
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $email,
'password' => $password,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$sessionCookie = $response['headers']['set-cookie'];
$sessionId2 = $response['body']['$id'];
// request was called in parallel and test failed
sleep(5);
/**
* List sessions
*/
$response = $this->client->call(Client::METHOD_GET, '/account/sessions', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'Cookie' => $sessionCookie,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$sessions = $response['body']['sessions'];
$this->assertEquals(1, count($sessions));
$this->assertEquals($sessionId2, $sessions[0]['$id']);
/**
* Reset Limit
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/max-sessions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 10,
]);
return $data;
}
public function testUpdateProjectServiceStatusAdmin(): array
{
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([