diff --git a/CHANGES.md b/CHANGES.md index 94b56bc7a0..ca647e7489 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +# Version 0.9.2 + +## Bugs + +- Fixed JWT session validation (#1408) +- Fixed passing valid JWT session to Cloud Functions (#1421) +- Fixed race condition when uploading and extracting bigger Cloud Functions (#1419) + # Version 0.9.1 ## Bugs diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index dd58108893..1560355aa0 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -748,20 +748,20 @@ App::post('/v1/functions/:functionId/executions') $jwt = ''; // initialize if (!empty($user->getId())) { // If userId exists, generate a JWT for function - $tokens = $user->getAttribute('tokens', []); - $session = new Document(); + $sessions = $user->getAttribute('sessions', []); + $current = new Document(); - foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $session = $token; + foreach ($sessions as $session) { /** @var Appwrite\Database\Document $session */ + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $current = $session; } } - if(!$session->isEmpty()) { + if(!$current->isEmpty()) { $jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. $jwt = $jwtObj->encode([ 'userId' => $user->getId(), - 'sessionId' => $session->getId(), + 'sessionId' => $current->getId(), ]); } } diff --git a/app/init.php b/app/init.php index 6461f50acb..872b1ba91a 100644 --- a/app/init.php +++ b/app/init.php @@ -47,8 +47,8 @@ const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s'; const APP_MODE_DEFAULT = 'default'; const APP_MODE_ADMIN = 'admin'; const APP_PAGING_LIMIT = 12; -const APP_CACHE_BUSTER = 149; -const APP_VERSION_STABLE = '0.9.1'; +const APP_CACHE_BUSTER = 150; +const APP_VERSION_STABLE = '0.9.2'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_CACHE = '/storage/cache'; diff --git a/app/workers/functions.php b/app/workers/functions.php index 227476add5..4c5d2cd3a8 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -376,7 +376,7 @@ class FunctionsV1 extends Worker $orchestration->setMemory($memory); $orchestration->setSwap($swap); - \array_walk($vars, function (string &$value, string $key) { + \array_walk($vars, function (string &$value) { $value = strval($value); }); @@ -385,19 +385,36 @@ class FunctionsV1 extends Worker $container, "", array( - 'sh', - '-c', - 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null' + 'tail', + '-f', + '/dev/null' ), "/usr/local/src", array(), - array(), + $vars, $tagPathTargetDir, array( "appwrite-type" => "function", "appwrite-created" => "{$executionTime}" )); + $tarStdout = ''; + $tarStderr = ''; + + $untarSuccess = $orchestration->execute($container, + array( + 'sh', + '-c', + 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz'), + $tarStdout, + $tarStderr, + $vars, + 60); + + if (!$untarSuccess) { + throw new Exception('Error untarring code'); + } + $executionEnd = \microtime(true); $list[$container] = new Container($container, @@ -405,7 +422,7 @@ class FunctionsV1 extends Worker "Up", array( 'appwrite-type' => 'function', - 'appwrite-created' => "{$executionTime}", + 'appwrite-created' => "{$executionTime}" )); Console::info("Function created in " . ($executionEnd - $executionStart) . " seconds"); diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index bd03e7d339..8eae515b9b 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -40,6 +40,7 @@ abstract class Migration '0.8.0' => 'V07', '0.9.0' => 'V08', '0.9.1' => 'V08', + '0.9.2' => 'V08', ]; /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 8ba16f8145..454a149c3c 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -185,7 +185,32 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(201, $execution['headers']['status-code']); + $executionId = $execution['body']['$id'] ?? ''; + sleep(10); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $apikey, + ]); + + $output = json_decode($executions['body']['stdout'], true); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $executions['body']['status']); + $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); + $this->assertEquals('Test', $output['APPWRITE_FUNCTION_NAME']); + $this->assertEquals($tagId, $output['APPWRITE_FUNCTION_TAG']); + $this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']); + $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); + $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']); + $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); + $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); + $this->assertNotEmpty($output['APPWRITE_FUNCTION_JWT']); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cf0a1cafc4..775eca06e2 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -582,8 +582,31 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(201, $execution['headers']['status-code']); + $executionId = $execution['body']['$id'] ?? ''; + sleep(10); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $output = json_decode($executions['body']['stdout'], true); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $executions['body']['status']); + $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); + $this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']); + $this->assertEquals($tagId, $output['APPWRITE_FUNCTION_TAG']); + $this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']); + $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); + $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']); + $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']); + $this->assertEmpty($output['APPWRITE_FUNCTION_JWT']); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/resources/functions/php-fn.tar.gz b/tests/resources/functions/php-fn.tar.gz index a97a2e2352..5e359122fa 100644 Binary files a/tests/resources/functions/php-fn.tar.gz and b/tests/resources/functions/php-fn.tar.gz differ diff --git a/tests/resources/functions/php-fn/index.php b/tests/resources/functions/php-fn/index.php index 32af3ccb16..449658e1e7 100644 --- a/tests/resources/functions/php-fn/index.php +++ b/tests/resources/functions/php-fn/index.php @@ -17,15 +17,18 @@ use Appwrite\Services\Storage; // $result = $storage->getFile($_ENV['APPWRITE_FILEID']); -echo $_ENV['APPWRITE_FUNCTION_ID']."\n"; -echo $_ENV['APPWRITE_FUNCTION_NAME']."\n"; -echo $_ENV['APPWRITE_FUNCTION_TAG']."\n"; -echo $_ENV['APPWRITE_FUNCTION_TRIGGER']."\n"; -echo $_ENV['APPWRITE_FUNCTION_RUNTIME_NAME']."\n"; -echo $_ENV['APPWRITE_FUNCTION_RUNTIME_VERSION']."\n"; -// echo $result['$id']; -echo $_ENV['APPWRITE_FUNCTION_EVENT']."\n"; -echo $_ENV['APPWRITE_FUNCTION_EVENT_DATA']."\n"; -echo 'data:'.$_ENV['APPWRITE_FUNCTION_DATA']."\n"; -echo 'userId:'.$_ENV['APPWRITE_FUNCTION_USER_ID']."\n"; -echo 'jwt:'.$_ENV['APPWRITE_FUNCTION_JWT']."\n"; +$output = [ + 'APPWRITE_FUNCTION_ID' => $_ENV['APPWRITE_FUNCTION_ID'], + 'APPWRITE_FUNCTION_NAME' => $_ENV['APPWRITE_FUNCTION_NAME'], + 'APPWRITE_FUNCTION_TAG' => $_ENV['APPWRITE_FUNCTION_TAG'], + 'APPWRITE_FUNCTION_TRIGGER' => $_ENV['APPWRITE_FUNCTION_TRIGGER'], + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $_ENV['APPWRITE_FUNCTION_RUNTIME_NAME'], + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $_ENV['APPWRITE_FUNCTION_RUNTIME_VERSION'], + 'APPWRITE_FUNCTION_EVENT' => $_ENV['APPWRITE_FUNCTION_EVENT'], + 'APPWRITE_FUNCTION_EVENT_DATA' => $_ENV['APPWRITE_FUNCTION_EVENT_DATA'], + 'APPWRITE_FUNCTION_DATA' => $_ENV['APPWRITE_FUNCTION_DATA'], + 'APPWRITE_FUNCTION_USER_ID' => $_ENV['APPWRITE_FUNCTION_USER_ID'], + 'APPWRITE_FUNCTION_JWT' => $_ENV['APPWRITE_FUNCTION_JWT'], +]; + +echo json_encode($output);