mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
Merge pull request #8831 from appwrite/feat-add-secret-environment-vars
Add secret attribute to variables
This commit is contained in:
commit
bba9c3d57a
6 changed files with 164 additions and 8 deletions
|
|
@ -4031,6 +4031,17 @@ $projectCollections = array_merge([
|
|||
'array' => false,
|
||||
'filters' => ['encrypt']
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('secret'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
'type' => Database::VAR_STRING,
|
||||
|
|
|
|||
|
|
@ -2337,10 +2337,11 @@ App::post('/v1/functions/:functionId/variables')
|
|||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
->action(function (string $functionId, string $key, string $value, bool $secret, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
|
|
@ -2361,6 +2362,7 @@ App::post('/v1/functions/:functionId/variables')
|
|||
'resourceType' => 'function',
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'secret' => $secret,
|
||||
'search' => implode(' ', [$variableId, $function->getId(), $key, 'function']),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use Utopia\Database\Query;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
|
|
@ -322,11 +323,12 @@ App::post('/v1/project/variables')
|
|||
->label('sdk.response.model', Response::MODEL_VARIABLE)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $key, string $value, Document $project, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
->action(function (string $key, string $value, bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForConsole) {
|
||||
$variableId = ID::unique();
|
||||
|
||||
$variable = new Document([
|
||||
|
|
@ -341,6 +343,7 @@ App::post('/v1/project/variables')
|
|||
'resourceType' => 'project',
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'secret' => $secret,
|
||||
'search' => implode(' ', [$variableId, $key, 'project']),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
|
|||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Variable extends Model
|
||||
{
|
||||
|
|
@ -41,6 +42,12 @@ class Variable extends Model
|
|||
'default' => '',
|
||||
'example' => 'myPa$$word1',
|
||||
])
|
||||
->addRule('secret', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Variable secret flag. Secret variables can only be updated or deleted, but never read.',
|
||||
'default' => false,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('resourceType', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Service to which the variable belongs. Possible values are "project", "function"',
|
||||
|
|
@ -56,6 +63,21 @@ class Variable extends Model
|
|||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter
|
||||
*
|
||||
* @param Document $document
|
||||
* @return Document
|
||||
*/
|
||||
public function filter(Document $document): Document
|
||||
{
|
||||
$secret = $document->getAttribute('secret');
|
||||
if ($secret === true) {
|
||||
$document->setAttribute('value', null);
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
|
|
|
|||
|
|
@ -118,6 +118,22 @@ class FunctionsConsoleClientTest extends Scope
|
|||
|
||||
$variableId = $variable['body']['$id'];
|
||||
|
||||
// test for secret variable
|
||||
$variable = $this->createVariable(
|
||||
$data['functionId'],
|
||||
[
|
||||
'key' => 'APP_TEST_1',
|
||||
'value' => 'TESTINGVALUE_1',
|
||||
'secret' => true
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST_1', $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$secretVariableId = $variable['body']['$id'];
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
@ -157,7 +173,8 @@ class FunctionsConsoleClientTest extends Scope
|
|||
return array_merge(
|
||||
$data,
|
||||
[
|
||||
'variableId' => $variableId
|
||||
'variableId' => $variableId,
|
||||
'secretVariableId' => $secretVariableId
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
@ -177,10 +194,12 @@ class FunctionsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, sizeof($response['body']['variables']));
|
||||
$this->assertEquals(1, $response['body']['total']);
|
||||
$this->assertEquals(2, sizeof($response['body']['variables']));
|
||||
$this->assertEquals(2, $response['body']['total']);
|
||||
$this->assertEquals("APP_TEST", $response['body']['variables'][0]['key']);
|
||||
$this->assertEquals("TESTINGVALUE", $response['body']['variables'][0]['value']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['variables'][1]['key']);
|
||||
$this->assertEmpty($response['body']['variables'][1]['value']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
@ -207,6 +226,15 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST", $response['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUE", $response['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
@ -251,6 +279,27 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST_UPDATE", $variable['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_1',
|
||||
'value' => 'TESTINGVALUEUPDATED_1'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
@ -332,6 +381,13 @@ class FunctionsConsoleClientTest extends Scope
|
|||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
|
|||
|
|
@ -3814,6 +3814,23 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$variableId = $variable['body']['$id'];
|
||||
|
||||
// test for secret variable
|
||||
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_1',
|
||||
'value' => 'TESTINGVALUE_1',
|
||||
'secret' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST_1', $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$secretVariableId = $variable['body']['$id'];
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
@ -3857,6 +3874,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$data,
|
||||
[
|
||||
'variableId' => $variableId,
|
||||
'secretVariableId' => $secretVariableId
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
@ -3877,10 +3895,12 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(1, $response['body']['variables']);
|
||||
$this->assertEquals(1, $response['body']['total']);
|
||||
$this->assertCount(2, $response['body']['variables']);
|
||||
$this->assertEquals(2, $response['body']['total']);
|
||||
$this->assertEquals("APP_TEST", $response['body']['variables'][0]['key']);
|
||||
$this->assertEquals("TESTINGVALUE", $response['body']['variables'][0]['value']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['variables'][1]['key']);
|
||||
$this->assertEmpty($response['body']['variables'][1]['value']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
@ -3903,6 +3923,16 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST", $response['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUE", $response['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
@ -3950,6 +3980,29 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST_UPDATE", $variable['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_1',
|
||||
'value' => 'TESTINGVALUEUPDATED_1'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
|
|
@ -3957,8 +4010,9 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(1, $response['body']['variables']);
|
||||
$this->assertCount(2, $response['body']['variables']);
|
||||
$this->assertEquals("APP_TEST_UPDATE", $response['body']['variables'][0]['key']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $response['body']['variables'][1]['key']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
@ -4037,6 +4091,14 @@ class ProjectsConsoleClientTest extends Scope
|
|||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()));
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue