Merge branch '0.15.x' of https://github.com/appwrite/appwrite into 0.15.x
This commit is contained in:
commit
153ac5c60d
6 changed files with 112 additions and 5 deletions
|
@ -800,7 +800,7 @@ $collections = [
|
|||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
|
@ -837,6 +837,17 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => 'expire',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
|
|
@ -26,6 +26,7 @@ use Appwrite\Extend\Exception;
|
|||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
@ -775,9 +776,10 @@ App::post('/v1/projects/:projectId/keys')
|
|||
->param('projectId', null, new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('expire', 0, new Integer() , 'Key expiration time in Unix timestamp. Use 0 for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $name, array $scopes, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $name, array $scopes, int $expire, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
|
@ -792,6 +794,7 @@ App::post('/v1/projects/:projectId/keys')
|
|||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
'scopes' => $scopes,
|
||||
'expire' => $expire,
|
||||
'secret' => \bin2hex(\random_bytes(128)),
|
||||
]);
|
||||
|
||||
|
@ -882,9 +885,10 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
->param('keyId', null, new UID(), 'Key unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
|
||||
->param('expire', 0, new Integer() , 'Key expiration time in Unix timestamp. Use 0 for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, int $expire, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
|
@ -904,6 +908,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
$key
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('scopes', $scopes)
|
||||
->setAttribute('expire', $expire)
|
||||
;
|
||||
|
||||
$dbForConsole->updateDocument('keys', $key->getId(), $key);
|
||||
|
|
|
@ -279,6 +279,12 @@ App::init(function (App $utopia, Request $request, Response $response, Document
|
|||
$role = Auth::USER_ROLE_APP;
|
||||
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
|
||||
|
||||
$expire = $key->getAttribute('expire', 0);
|
||||
|
||||
if(!empty($expire) && $expire < \time()){
|
||||
throw new AppwriteException('Project key expired', 401, AppwriteException:: PROJECT_KEY_EXPIRED);
|
||||
}
|
||||
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_APP);
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ class Exception extends \Exception
|
|||
const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url';
|
||||
const PROJECT_MISSING_USER_ID = 'project_missing_user_id';
|
||||
const PROJECT_RESERVED_PROJECT = 'project_reserved_project';
|
||||
const PROJECT_KEY_EXPIRED = 'project_key_expired';
|
||||
|
||||
/** Webhooks */
|
||||
const WEBHOOK_NOT_FOUND = 'webhook_not_found';
|
||||
|
|
|
@ -27,6 +27,12 @@ class Key extends Model
|
|||
'default' => '',
|
||||
'example' => 'My API Key',
|
||||
])
|
||||
->addRule('expire', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Key expiration in Unix timestamp.',
|
||||
'default' => 0,
|
||||
'example' => '1653990687',
|
||||
])
|
||||
->addRule('scopes', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Allowed permission scopes.',
|
||||
|
|
|
@ -1162,7 +1162,10 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertContains('teams.write', $response['body']['scopes']);
|
||||
$this->assertNotEmpty($response['body']['secret']);
|
||||
|
||||
$data = array_merge($data, ['keyId' => $response['body']['$id']]);
|
||||
$data = array_merge($data, [
|
||||
'keyId' => $response['body']['$id'],
|
||||
'secret' => $response['body']['secret']
|
||||
]);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
@ -1180,6 +1183,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateProjectKey
|
||||
*/
|
||||
|
@ -1192,6 +1196,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['total']);
|
||||
|
||||
|
@ -1202,6 +1207,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateProjectKey
|
||||
*/
|
||||
|
@ -1213,6 +1219,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/keys/' . $keyId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $keyId
|
||||
], $this->getHeaders()), []);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
@ -1237,6 +1244,76 @@ class ProjectsConsoleClientTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateProject
|
||||
*/
|
||||
public function testValidateProjectKey($data): void
|
||||
{
|
||||
$id = $data['projectId'] ?? '';
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/keys', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Key Test',
|
||||
'scopes' => ['health.read'],
|
||||
'expire' => time()+3600,
|
||||
]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health' , [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
'x-appwrite-key' => $response['body']['secret']
|
||||
], []);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/keys', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Key Test',
|
||||
'scopes' => ['health.read'],
|
||||
'expire' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health' , [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
'x-appwrite-key' => $response['body']['secret']
|
||||
], []);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/keys', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Key Test',
|
||||
'scopes' => ['health.read'],
|
||||
'expire' => time()-3600,
|
||||
]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/health' , [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $id,
|
||||
'x-appwrite-key' => $response['body']['secret']
|
||||
], []);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateProjectKey
|
||||
*/
|
||||
|
@ -1251,6 +1328,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'Key Test Update',
|
||||
'scopes' => ['users.read', 'users.write', 'collections.read'],
|
||||
'expire' => time()+360,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
Loading…
Reference in a new issue