From dd9cbcef6233d8b949f150de089262ef5dddd568 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 30 Dec 2020 01:00:44 +0200 Subject: [PATCH] Enabled client endpoints for CF --- app/controllers/api/functions.php | 26 ++++-- .../Functions/FunctionsCustomClientTest.php | 75 ++++++++++++++++++ .../Functions/FunctionsCustomServerTest.php | 2 +- tests/resources/functions/deno-fx.tar.gz | Bin 238 -> 0 bytes 4 files changed, 96 insertions(+), 7 deletions(-) delete mode 100644 tests/resources/functions/deno-fx.tar.gz diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 2e696ddc47..f558dca073 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -2,6 +2,7 @@ use Appwrite\Database\Database; use Appwrite\Database\Document; +use Appwrite\Database\Validator\Authorization; use Appwrite\Database\Validator\UID; use Appwrite\Storage\Storage; use Appwrite\Storage\Validator\File; @@ -18,6 +19,7 @@ use Utopia\Validator\Range; use Utopia\Validator\WhiteList; use Utopia\Config\Config; use Cron\CronExpression; +use Utopia\Exception; include_once __DIR__ . '/../shared/api.php'; @@ -33,6 +35,7 @@ App::post('/v1/functions') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FUNCTION) ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.') + ->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.') ->param('env', '', new WhiteList(array_keys(Config::getParam('environments')), true), 'Execution enviornment.') ->param('vars', [], new Assoc(), 'Key-value JSON object.', true) ->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true) @@ -40,12 +43,13 @@ App::post('/v1/functions') ->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true) ->inject('response') ->inject('projectDB') - ->action(function ($name, $env, $vars, $events, $schedule, $timeout, $response, $projectDB) { + ->action(function ($name, $execute, $env, $vars, $events, $schedule, $timeout, $response, $projectDB) { $function = $projectDB->createDocument([ '$collection' => Database::SYSTEM_COLLECTION_FUNCTIONS, '$permissions' => [ 'read' => [], 'write' => [], + 'execute' => $execute, ], 'dateCreated' => time(), 'dateUpdated' => time(), @@ -613,7 +617,7 @@ App::post('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('Create Execution') ->label('scope', 'functions.write') - ->label('sdk.platform', [APP_PLATFORM_SERVER]) + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'createExecution') ->label('sdk.description', '/docs/references/functions/create-execution.md') @@ -630,6 +634,8 @@ App::post('/v1/functions/:functionId/executions') /** @var Appwrite\Database\Document $project */ /** @var Appwrite\Database\Database $projectDB */ + Authorization::disable(); + $function = $projectDB->getDocument($functionId); if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) { @@ -645,11 +651,17 @@ App::post('/v1/functions/:functionId/executions') if (empty($tag->getId()) || Database::SYSTEM_COLLECTION_TAGS != $tag->getCollection()) { throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404); } - + + $validator = new Authorization($function, 'execute'); + + if (!$validator->isValid($function->getPermissions())) { // Check if user has write access to execute function + throw new Exception($validator->getDescription(), 401); + } + $execution = $projectDB->createDocument([ '$collection' => Database::SYSTEM_COLLECTION_EXECUTIONS, '$permissions' => [ - 'read' => [], + 'read' => $function->getPermissions(), 'write' => [], ], 'dateCreated' => time(), @@ -662,6 +674,8 @@ App::post('/v1/functions/:functionId/executions') 'time' => 0, ]); + Authorization::reset(); + if (false === $execution) { throw new Exception('Failed saving execution to DB', 500); } @@ -683,7 +697,7 @@ App::get('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('List Executions') ->label('scope', 'functions.read') - ->label('sdk.platform', [APP_PLATFORM_SERVER]) + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'listExecutions') ->label('sdk.description', '/docs/references/functions/list-executions.md') @@ -724,7 +738,7 @@ App::get('/v1/functions/:functionId/executions/:executionId') ->groups(['api', 'functions']) ->desc('Get Execution') ->label('scope', 'functions.read') - ->label('sdk.platform', [APP_PLATFORM_SERVER]) + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'getExecution') ->label('sdk.description', '/docs/references/functions/get-execution.md') diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index e0c6e1177d..25d3a8237c 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Services\Functions; +use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -40,4 +41,78 @@ class FunctionsCustomClientTest extends Scope return []; } + + public function testCreateExecution():array + { + /** + * Test for SUCCESS + */ + $function = $this->client->call(Client::METHOD_POST, '/functions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'name' => 'Test', + 'execute' => ['user:'.$this->getUser()['$id']], + 'env' => 'php-7.4', + 'vars' => [ + 'funcKey1' => 'funcValue1', + 'funcKey2' => 'funcValue2', + 'funcKey3' => 'funcValue3', + ], + 'events' => [ + 'account.create', + 'account.delete', + ], + 'schedule' => '* * * * *', + 'timeout' => 10, + ]); + + $this->assertEquals(201, $function['headers']['status-code']); + + $tag = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/tags', [ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'command' => 'php function.php', + 'code' => new CURLFile(realpath(__DIR__ . '/../../../resources/functions/php.tar.gz'), 'application/x-gzip', 'php-fx.tar.gz'), + ]); + + $tagId = $tag['body']['$id'] ?? ''; + + $this->assertEquals(201, $tag['headers']['status-code']); + + $function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$function['body']['$id'].'/tag', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'tag' => $tagId, + ]); + + $this->assertEquals(200, $function['headers']['status-code']); + + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'async' => 1, + ]); + + $this->assertEquals(401, $execution['headers']['status-code']); + + $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'async' => 1, + ]); + + $executionId = $execution['body']['$id'] ?? ''; + + $this->assertEquals(201, $execution['headers']['status-code']); + + return []; + } } \ No newline at end of file diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f44f121a01..cca171561e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -591,7 +591,7 @@ class FunctionsCustomServerTest extends Scope $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(201, $execution['headers']['status-code']); - sleep(15); + sleep(20); $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', diff --git a/tests/resources/functions/deno-fx.tar.gz b/tests/resources/functions/deno-fx.tar.gz deleted file mode 100644 index 3a6033ed52c383b27f6bc72ff99d4cf827452c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmVd6Q=ck={c|`96QH_%nYYnv}m3D&@~s z^7Fz!&%Yl|I7Ya@0fXz0a0NHw0M%!Vr)f*6u>pzif}v+>Fxv1%2Q=xgR3BxT&P?bn o@p!DOo%J4JkA5`O=G*4dyxyes-yS%V$z(EjZvOM#g#Zcw0G(uZn*aa+