mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Reuse resource tokens for orchestrator artifacts
This commit is contained in:
@@ -485,6 +485,7 @@ const ADVISOR_REPORT_TYPES = [
|
||||
|
||||
// Resource types for Tokens
|
||||
const TOKENS_RESOURCE_TYPE_FILES = 'files';
|
||||
const TOKENS_RESOURCE_TYPE_DEPLOYMENT_ARTIFACTS = 'deploymentArtifacts';
|
||||
const TOKENS_RESOURCE_TYPE_SITES = 'sites';
|
||||
const TOKENS_RESOURCE_TYPE_FUNCTIONS = 'functions';
|
||||
const TOKENS_RESOURCE_TYPE_DATABASES = 'databases';
|
||||
|
||||
@@ -1243,6 +1243,31 @@ return function (Container $context): void {
|
||||
'fileInternalId' => $sequences[1],
|
||||
]);
|
||||
})(),
|
||||
TOKENS_RESOURCE_TYPE_DEPLOYMENT_ARTIFACTS => (function () use ($token, $dbForProject, $authorization) {
|
||||
$sequences = explode(':', $token->getAttribute('resourceInternalId'));
|
||||
$ids = explode(':', $token->getAttribute('resourceId'));
|
||||
|
||||
if (count($sequences) !== 2 || count($ids) !== 4) {
|
||||
return new Document([]);
|
||||
}
|
||||
|
||||
$accessedAt = $token->getAttribute('accessedAt', 0);
|
||||
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
|
||||
$token->setAttribute('accessedAt', DatabaseDateTime::now());
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), new Document([
|
||||
'accessedAt' => $token->getAttribute('accessedAt')
|
||||
])));
|
||||
}
|
||||
|
||||
return new Document([
|
||||
'resourceType' => $ids[0],
|
||||
'resourceId' => $ids[1],
|
||||
'deploymentId' => $ids[2],
|
||||
'purpose' => $ids[3],
|
||||
'resourceInternalId' => $sequences[0],
|
||||
'deploymentInternalId' => $sequences[1],
|
||||
]);
|
||||
})(),
|
||||
|
||||
default => throw new Exception(Exception::TOKEN_RESOURCE_TYPE_INVALID),
|
||||
};
|
||||
|
||||
Generated
+4
-4
@@ -1230,12 +1230,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/open-runtimes/orchestrator-client-php.git",
|
||||
"reference": "5b44ad8f056d180e63368f844d01d167f706b13e"
|
||||
"reference": "85d933c3d56467b2fbc09eac7813de7c2b1c47b1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/open-runtimes/orchestrator-client-php/zipball/5b44ad8f056d180e63368f844d01d167f706b13e",
|
||||
"reference": "5b44ad8f056d180e63368f844d01d167f706b13e",
|
||||
"url": "https://api.github.com/repos/open-runtimes/orchestrator-client-php/zipball/85d933c3d56467b2fbc09eac7813de7c2b1c47b1",
|
||||
"reference": "85d933c3d56467b2fbc09eac7813de7c2b1c47b1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1294,7 +1294,7 @@
|
||||
"source": "https://github.com/open-runtimes/orchestrator-client-php/tree/add-jobs-sdk",
|
||||
"issues": "https://github.com/open-runtimes/orchestrator-client-php/issues"
|
||||
},
|
||||
"time": "2026-05-22T18:46:15+00:00"
|
||||
"time": "2026-05-23T03:41:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/api",
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Builds;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
|
||||
class OrchestratorToken
|
||||
{
|
||||
public static function create(string $projectId, string $resourceId, string $deploymentId, string $purpose, int $ttl = 3600): string
|
||||
{
|
||||
$expires = \time() + $ttl;
|
||||
$payload = [
|
||||
'projectId' => $projectId,
|
||||
'resourceId' => $resourceId,
|
||||
'deploymentId' => $deploymentId,
|
||||
'purpose' => $purpose,
|
||||
'expires' => $expires,
|
||||
];
|
||||
|
||||
$encoded = self::base64UrlEncode(\json_encode($payload, JSON_THROW_ON_ERROR));
|
||||
$signature = self::sign($encoded);
|
||||
|
||||
return $encoded . '.' . $signature;
|
||||
}
|
||||
|
||||
public static function verify(string $token, string $projectId, string $resourceId, string $deploymentId, string $purpose): void
|
||||
{
|
||||
[$encoded, $signature] = \array_pad(\explode('.', $token, 2), 2, '');
|
||||
|
||||
if (empty($encoded) || empty($signature) || !\hash_equals(self::sign($encoded), $signature)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
$payload = \json_decode(self::base64UrlDecode($encoded), true);
|
||||
if (!\is_array($payload)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
if (($payload['expires'] ?? 0) < \time()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token expired.');
|
||||
}
|
||||
|
||||
if (
|
||||
($payload['projectId'] ?? '') !== $projectId ||
|
||||
($payload['resourceId'] ?? '') !== $resourceId ||
|
||||
($payload['deploymentId'] ?? '') !== $deploymentId ||
|
||||
($payload['purpose'] ?? '') !== $purpose
|
||||
) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token mismatch.');
|
||||
}
|
||||
}
|
||||
|
||||
public static function verifySignature(string $body, string $signature): void
|
||||
{
|
||||
$expected = 'sha256=' . \hash_hmac('sha256', $body, self::secret());
|
||||
|
||||
if (empty($signature) || !\hash_equals($expected, $signature)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid orchestrator event signature.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function sign(string $payload): string
|
||||
{
|
||||
return \hash_hmac('sha256', $payload, self::secret());
|
||||
}
|
||||
|
||||
private static function secret(): string
|
||||
{
|
||||
$secret = \Utopia\System\System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', '');
|
||||
|
||||
if (empty($secret)) {
|
||||
$secret = \Utopia\System\System::getEnv('_APP_OPENSSL_KEY_V1', '');
|
||||
}
|
||||
|
||||
return $secret;
|
||||
}
|
||||
|
||||
private static function base64UrlEncode(string $value): string
|
||||
{
|
||||
return \rtrim(\strtr(\base64_encode($value), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
private static function base64UrlDecode(string $value): string
|
||||
{
|
||||
$value .= \str_repeat('=', (4 - \strlen($value) % 4) % 4);
|
||||
|
||||
return \base64_decode(\strtr($value, '-_', '+/')) ?: '';
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Artifacts\Build;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Http\Deployments\Artifacts\Build\ChunkedBuildArtifact;
|
||||
@@ -43,6 +42,7 @@ class Update extends Action
|
||||
->inject('request')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForBuilds')
|
||||
->inject('publisherForBuilds')
|
||||
->inject('cache')
|
||||
@@ -58,13 +58,13 @@ class Update extends Action
|
||||
Request $request,
|
||||
Database $dbForProject,
|
||||
Document $project,
|
||||
Document $resourceToken,
|
||||
Device $deviceForBuilds,
|
||||
BuildPublisher $publisherForBuilds,
|
||||
Cache $cache,
|
||||
callable $locks
|
||||
) {
|
||||
$token = $token ?: $request->getQuery('token', '');
|
||||
OrchestratorToken::verify($token, $project->getId(), $functionId, $deploymentId, 'build');
|
||||
$this->verifyArtifactToken($resourceToken, RESOURCE_TYPE_FUNCTIONS, $functionId, $deploymentId, 'build');
|
||||
|
||||
$function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
if ($function->isEmpty()) {
|
||||
@@ -90,4 +90,20 @@ class Update extends Action
|
||||
locks: $locks
|
||||
);
|
||||
}
|
||||
|
||||
private function verifyArtifactToken(Document $resourceToken, string $resourceType, string $resourceId, string $deploymentId, string $purpose): void
|
||||
{
|
||||
if ($resourceToken->isEmpty()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
if (
|
||||
$resourceToken->getAttribute('resourceType') !== $resourceType ||
|
||||
$resourceToken->getAttribute('resourceId') !== $resourceId ||
|
||||
$resourceToken->getAttribute('deploymentId') !== $deploymentId ||
|
||||
$resourceToken->getAttribute('purpose') !== $purpose
|
||||
) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token mismatch.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Artifacts\Source;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
@@ -37,6 +36,7 @@ class Get extends Action
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFunctions')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
@@ -48,9 +48,10 @@ class Get extends Action
|
||||
Response $response,
|
||||
Database $dbForProject,
|
||||
Document $project,
|
||||
Document $resourceToken,
|
||||
Device $deviceForFunctions
|
||||
) {
|
||||
OrchestratorToken::verify($token, $project->getId(), $functionId, $deploymentId, 'source');
|
||||
$this->verifyArtifactToken($resourceToken, RESOURCE_TYPE_FUNCTIONS, $functionId, $deploymentId, 'source');
|
||||
|
||||
$function = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
if ($function->isEmpty()) {
|
||||
@@ -89,4 +90,20 @@ class Get extends Action
|
||||
|
||||
$response->send($deviceForFunctions->read($path));
|
||||
}
|
||||
|
||||
private function verifyArtifactToken(Document $resourceToken, string $resourceType, string $resourceId, string $deploymentId, string $purpose): void
|
||||
{
|
||||
if ($resourceToken->isEmpty()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
if (
|
||||
$resourceToken->getAttribute('resourceType') !== $resourceType ||
|
||||
$resourceToken->getAttribute('resourceId') !== $resourceId ||
|
||||
$resourceToken->getAttribute('deploymentId') !== $deploymentId ||
|
||||
$resourceToken->getAttribute('purpose') !== $purpose
|
||||
) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token mismatch.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Events;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Event\Message\Build as BuildMessage;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use OpenRuntimes\Orchestrator\Callback\Signature;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Request;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
@@ -52,7 +53,10 @@ class Create extends Action
|
||||
BuildPublisher $publisherForBuilds
|
||||
) {
|
||||
$body = $request->getRawPayload();
|
||||
OrchestratorToken::verifySignature($body, $request->getHeader('x-signature-256', ''));
|
||||
$secret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', ''));
|
||||
if (! Signature::verify($body, $request->getHeader('x-signature-256', ''), $secret)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid orchestrator event signature.');
|
||||
}
|
||||
|
||||
$event = \json_decode($body, true);
|
||||
if (!\is_array($event)) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Appwrite\Platform\Modules\Functions\Workers;
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Message\Usage as UsageMessage;
|
||||
@@ -16,6 +15,7 @@ use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Filter\BranchDomain as BranchDomainFilter;
|
||||
use Appwrite\Usage\Context;
|
||||
use Appwrite\Utopia\Response\Model\Deployment;
|
||||
use Appwrite\Utopia\Response\Model\ResourceToken as ResourceTokenModel;
|
||||
use Appwrite\Vcs\Comment;
|
||||
use Exception;
|
||||
use Executor\Exception\Timeout as ExecutorTimeout;
|
||||
@@ -29,6 +29,7 @@ use OpenRuntimes\Orchestrator\DTO\JobRequest;
|
||||
use OpenRuntimes\Orchestrator\Enum\CallbackEvent;
|
||||
use OpenRuntimes\Orchestrator\Exception\TimeoutException as OrchestratorTimeout;
|
||||
use Swoole\Coroutine as Co;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Console;
|
||||
@@ -39,6 +40,7 @@ use Utopia\Database\Exception\Conflict;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Restricted;
|
||||
use Utopia\Database\Exception\Structure;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Detector\Detection\Rendering\SSR;
|
||||
use Utopia\Detector\Detection\Rendering\XStatic;
|
||||
@@ -722,6 +724,7 @@ class Builds extends Action
|
||||
if ($buildsBackend === 'orchestrator') {
|
||||
$this->createOrchestratorBuild(
|
||||
project: $project,
|
||||
dbForProject: $dbForProject,
|
||||
resource: $resource,
|
||||
deployment: $deployment,
|
||||
runtime: $runtime,
|
||||
@@ -1326,6 +1329,7 @@ class Builds extends Action
|
||||
|
||||
protected function createOrchestratorBuild(
|
||||
Document $project,
|
||||
Database $dbForProject,
|
||||
Document $resource,
|
||||
Document $deployment,
|
||||
array $runtime,
|
||||
@@ -1344,8 +1348,9 @@ class Builds extends Action
|
||||
$callbackBase = \rtrim(System::getEnv('_APP_ORCHESTRATOR_APPWRITE_CALLBACK_ENDPOINT', $base), '/');
|
||||
$resourcePath = $resource->getCollection();
|
||||
|
||||
$sourceToken = OrchestratorToken::create($project->getId(), $resourceId, $deploymentId, 'source', $timeout + 300);
|
||||
$buildToken = OrchestratorToken::create($project->getId(), $resourceId, $deploymentId, 'build', $timeout + 300);
|
||||
$callbackSecret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', ''));
|
||||
$sourceToken = $this->createDeploymentArtifactToken($dbForProject, $resource, $deployment, 'source', $timeout + 300);
|
||||
$buildToken = $this->createDeploymentArtifactToken($dbForProject, $resource, $deployment, 'build', $timeout + 300);
|
||||
$projectQuery = 'project=' . \rawurlencode($project->getId());
|
||||
$appwriteProjectHeader = ['X-Appwrite-Project' => $project->getId()];
|
||||
|
||||
@@ -1443,7 +1448,7 @@ class Builds extends Action
|
||||
CallbackEvent::Artifact,
|
||||
CallbackEvent::Exit,
|
||||
],
|
||||
key: System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', '')),
|
||||
key: $callbackSecret,
|
||||
headers: $appwriteProjectHeader,
|
||||
),
|
||||
), $timeout);
|
||||
@@ -1452,6 +1457,25 @@ class Builds extends Action
|
||||
}
|
||||
}
|
||||
|
||||
protected function createDeploymentArtifactToken(
|
||||
Database $dbForProject,
|
||||
Document $resource,
|
||||
Document $deployment,
|
||||
string $purpose,
|
||||
int $duration
|
||||
): string {
|
||||
$token = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->createDocument('resourceTokens', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'secret' => (new Token(128))->generate(),
|
||||
'resourceId' => "{$resource->getCollection()}:{$resource->getId()}:{$deployment->getId()}:{$purpose}",
|
||||
'resourceInternalId' => "{$resource->getSequence()}:{$deployment->getSequence()}",
|
||||
'resourceType' => TOKENS_RESOURCE_TYPE_DEPLOYMENT_ARTIFACTS,
|
||||
'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)),
|
||||
])));
|
||||
|
||||
return (new ResourceTokenModel())->filter($token)->getAttribute('secret');
|
||||
}
|
||||
|
||||
protected function applyOrchestratorEvent(
|
||||
Realtime $queueForRealtime,
|
||||
Context $usage,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Artifacts\Build;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Http\Deployments\Artifacts\Build\ChunkedBuildArtifact;
|
||||
@@ -43,6 +42,7 @@ class Update extends Action
|
||||
->inject('request')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForBuilds')
|
||||
->inject('publisherForBuilds')
|
||||
->inject('cache')
|
||||
@@ -58,13 +58,13 @@ class Update extends Action
|
||||
Request $request,
|
||||
Database $dbForProject,
|
||||
Document $project,
|
||||
Document $resourceToken,
|
||||
Device $deviceForBuilds,
|
||||
BuildPublisher $publisherForBuilds,
|
||||
Cache $cache,
|
||||
callable $locks
|
||||
) {
|
||||
$token = $token ?: $request->getQuery('token', '');
|
||||
OrchestratorToken::verify($token, $project->getId(), $siteId, $deploymentId, 'build');
|
||||
$this->verifyArtifactToken($resourceToken, RESOURCE_TYPE_SITES, $siteId, $deploymentId, 'build');
|
||||
|
||||
$site = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('sites', $siteId));
|
||||
if ($site->isEmpty()) {
|
||||
@@ -90,4 +90,20 @@ class Update extends Action
|
||||
locks: $locks
|
||||
);
|
||||
}
|
||||
|
||||
private function verifyArtifactToken(Document $resourceToken, string $resourceType, string $resourceId, string $deploymentId, string $purpose): void
|
||||
{
|
||||
if ($resourceToken->isEmpty()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
if (
|
||||
$resourceToken->getAttribute('resourceType') !== $resourceType ||
|
||||
$resourceToken->getAttribute('resourceId') !== $resourceId ||
|
||||
$resourceToken->getAttribute('deploymentId') !== $deploymentId ||
|
||||
$resourceToken->getAttribute('purpose') !== $purpose
|
||||
) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token mismatch.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Artifacts\Source;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
@@ -37,6 +36,7 @@ class Get extends Action
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForSites')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
@@ -48,9 +48,10 @@ class Get extends Action
|
||||
Response $response,
|
||||
Database $dbForProject,
|
||||
Document $project,
|
||||
Document $resourceToken,
|
||||
Device $deviceForSites
|
||||
) {
|
||||
OrchestratorToken::verify($token, $project->getId(), $siteId, $deploymentId, 'source');
|
||||
$this->verifyArtifactToken($resourceToken, RESOURCE_TYPE_SITES, $siteId, $deploymentId, 'source');
|
||||
|
||||
$site = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument('sites', $siteId));
|
||||
if ($site->isEmpty()) {
|
||||
@@ -89,4 +90,20 @@ class Get extends Action
|
||||
|
||||
$response->send($deviceForSites->read($path));
|
||||
}
|
||||
|
||||
private function verifyArtifactToken(Document $resourceToken, string $resourceType, string $resourceId, string $deploymentId, string $purpose): void
|
||||
{
|
||||
if ($resourceToken->isEmpty()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid build artifact token.');
|
||||
}
|
||||
|
||||
if (
|
||||
$resourceToken->getAttribute('resourceType') !== $resourceType ||
|
||||
$resourceToken->getAttribute('resourceId') !== $resourceId ||
|
||||
$resourceToken->getAttribute('deploymentId') !== $deploymentId ||
|
||||
$resourceToken->getAttribute('purpose') !== $purpose
|
||||
) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Build artifact token mismatch.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Events;
|
||||
|
||||
use Appwrite\Builds\OrchestratorToken;
|
||||
use Appwrite\Event\Message\Build as BuildMessage;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use OpenRuntimes\Orchestrator\Callback\Signature;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Request;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
@@ -52,7 +53,10 @@ class Create extends Action
|
||||
BuildPublisher $publisherForBuilds
|
||||
) {
|
||||
$body = $request->getRawPayload();
|
||||
OrchestratorToken::verifySignature($body, $request->getHeader('x-signature-256', ''));
|
||||
$secret = System::getEnv('_APP_ORCHESTRATOR_CALLBACK_SECRET', System::getEnv('_APP_OPENSSL_KEY_V1', ''));
|
||||
if (! Signature::verify($body, $request->getHeader('x-signature-256', ''), $secret)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'Invalid orchestrator event signature.');
|
||||
}
|
||||
|
||||
$event = \json_decode($body, true);
|
||||
if (!\is_array($event)) {
|
||||
|
||||
Reference in New Issue
Block a user