Merge pull request #8831 from appwrite/feat-add-secret-environment-vars

Add secret attribute to variables
This commit is contained in:
Torsten Dittmann 2024-10-24 12:53:10 +02:00 committed by GitHub
commit bba9c3d57a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 164 additions and 8 deletions

View file

@ -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,

View file

@ -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']),
]);

View file

@ -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']),
]);

View file

@ -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
*

View file

@ -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'],

View file

@ -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'],