diff --git a/apps/webhook_listeners/appinfo/info.xml b/apps/webhook_listeners/appinfo/info.xml index fae9f417aba31..65a02f0299762 100644 --- a/apps/webhook_listeners/appinfo/info.xml +++ b/apps/webhook_listeners/appinfo/info.xml @@ -18,7 +18,7 @@ Administrators can configure webhook listeners via the app's OCS API. The app al ]]> - 1.4.1 + 1.5.0 agpl Côme Chilliet WebhookListeners @@ -49,4 +49,8 @@ Administrators can configure webhook listeners via the app's OCS API. The app al OCA\WebhookListeners\Settings\Admin OCA\WebhookListeners\Settings\AdminSection + + + OCA\WebhookListeners\BackgroundJobs\WebhookTokenCleanup + diff --git a/apps/webhook_listeners/composer/composer/autoload_classmap.php b/apps/webhook_listeners/composer/composer/autoload_classmap.php index 959b4c1d2ae25..22349364e08aa 100644 --- a/apps/webhook_listeners/composer/composer/autoload_classmap.php +++ b/apps/webhook_listeners/composer/composer/autoload_classmap.php @@ -9,16 +9,21 @@ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'OCA\\WebhookListeners\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', 'OCA\\WebhookListeners\\BackgroundJobs\\WebhookCall' => $baseDir . '/../lib/BackgroundJobs/WebhookCall.php', + 'OCA\\WebhookListeners\\BackgroundJobs\\WebhookTokenCleanup' => $baseDir . '/../lib/BackgroundJobs/WebhookTokenCleanup.php', 'OCA\\WebhookListeners\\Command\\ListWebhooks' => $baseDir . '/../lib/Command/ListWebhooks.php', 'OCA\\WebhookListeners\\Controller\\WebhooksController' => $baseDir . '/../lib/Controller/WebhooksController.php', 'OCA\\WebhookListeners\\Db\\AuthMethod' => $baseDir . '/../lib/Db/AuthMethod.php', + 'OCA\\WebhookListeners\\Db\\EphemeralToken' => $baseDir . '/../lib/Db/EphemeralToken.php', + 'OCA\\WebhookListeners\\Db\\EphemeralTokenMapper' => $baseDir . '/../lib/Db/EphemeralTokenMapper.php', 'OCA\\WebhookListeners\\Db\\WebhookListener' => $baseDir . '/../lib/Db/WebhookListener.php', 'OCA\\WebhookListeners\\Db\\WebhookListenerMapper' => $baseDir . '/../lib/Db/WebhookListenerMapper.php', 'OCA\\WebhookListeners\\Listener\\WebhooksEventListener' => $baseDir . '/../lib/Listener/WebhooksEventListener.php', 'OCA\\WebhookListeners\\Migration\\Version1000Date20240527153425' => $baseDir . '/../lib/Migration/Version1000Date20240527153425.php', 'OCA\\WebhookListeners\\Migration\\Version1001Date20240716184935' => $baseDir . '/../lib/Migration/Version1001Date20240716184935.php', + 'OCA\\WebhookListeners\\Migration\\Version1500Date20251007130000' => $baseDir . '/../lib/Migration/Version1500Date20251007130000.php', 'OCA\\WebhookListeners\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php', 'OCA\\WebhookListeners\\Service\\PHPMongoQuery' => $baseDir . '/../lib/Service/PHPMongoQuery.php', + 'OCA\\WebhookListeners\\Service\\TokenService' => $baseDir . '/../lib/Service/TokenService.php', 'OCA\\WebhookListeners\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php', 'OCA\\WebhookListeners\\Settings\\AdminSection' => $baseDir . '/../lib/Settings/AdminSection.php', ); diff --git a/apps/webhook_listeners/composer/composer/autoload_static.php b/apps/webhook_listeners/composer/composer/autoload_static.php index b9b367315f66f..3527838629b3a 100644 --- a/apps/webhook_listeners/composer/composer/autoload_static.php +++ b/apps/webhook_listeners/composer/composer/autoload_static.php @@ -24,16 +24,21 @@ class ComposerStaticInitWebhookListeners 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'OCA\\WebhookListeners\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', 'OCA\\WebhookListeners\\BackgroundJobs\\WebhookCall' => __DIR__ . '/..' . '/../lib/BackgroundJobs/WebhookCall.php', + 'OCA\\WebhookListeners\\BackgroundJobs\\WebhookTokenCleanup' => __DIR__ . '/..' . '/../lib/BackgroundJobs/WebhookTokenCleanup.php', 'OCA\\WebhookListeners\\Command\\ListWebhooks' => __DIR__ . '/..' . '/../lib/Command/ListWebhooks.php', 'OCA\\WebhookListeners\\Controller\\WebhooksController' => __DIR__ . '/..' . '/../lib/Controller/WebhooksController.php', 'OCA\\WebhookListeners\\Db\\AuthMethod' => __DIR__ . '/..' . '/../lib/Db/AuthMethod.php', + 'OCA\\WebhookListeners\\Db\\EphemeralToken' => __DIR__ . '/..' . '/../lib/Db/EphemeralToken.php', + 'OCA\\WebhookListeners\\Db\\EphemeralTokenMapper' => __DIR__ . '/..' . '/../lib/Db/EphemeralTokenMapper.php', 'OCA\\WebhookListeners\\Db\\WebhookListener' => __DIR__ . '/..' . '/../lib/Db/WebhookListener.php', 'OCA\\WebhookListeners\\Db\\WebhookListenerMapper' => __DIR__ . '/..' . '/../lib/Db/WebhookListenerMapper.php', 'OCA\\WebhookListeners\\Listener\\WebhooksEventListener' => __DIR__ . '/..' . '/../lib/Listener/WebhooksEventListener.php', 'OCA\\WebhookListeners\\Migration\\Version1000Date20240527153425' => __DIR__ . '/..' . '/../lib/Migration/Version1000Date20240527153425.php', 'OCA\\WebhookListeners\\Migration\\Version1001Date20240716184935' => __DIR__ . '/..' . '/../lib/Migration/Version1001Date20240716184935.php', + 'OCA\\WebhookListeners\\Migration\\Version1500Date20251007130000' => __DIR__ . '/..' . '/../lib/Migration/Version1500Date20251007130000.php', 'OCA\\WebhookListeners\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php', 'OCA\\WebhookListeners\\Service\\PHPMongoQuery' => __DIR__ . '/..' . '/../lib/Service/PHPMongoQuery.php', + 'OCA\\WebhookListeners\\Service\\TokenService' => __DIR__ . '/..' . '/../lib/Service/TokenService.php', 'OCA\\WebhookListeners\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php', 'OCA\\WebhookListeners\\Settings\\AdminSection' => __DIR__ . '/..' . '/../lib/Settings/AdminSection.php', ); diff --git a/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php b/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php index c8d06ca70471e..d9a2cf532adce 100644 --- a/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php +++ b/apps/webhook_listeners/lib/BackgroundJobs/WebhookCall.php @@ -12,6 +12,7 @@ use OCA\AppAPI\PublicFunctions; use OCA\WebhookListeners\Db\AuthMethod; use OCA\WebhookListeners\Db\WebhookListenerMapper; +use OCA\WebhookListeners\Service\TokenService; use OCP\App\IAppManager; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\QueuedJob; @@ -30,6 +31,7 @@ public function __construct( private WebhookListenerMapper $mapper, private LoggerInterface $logger, private IAppManager $appManager, + private TokenService $tokenService, ITimeFactory $timeFactory, ) { parent::__construct($timeFactory); @@ -42,6 +44,9 @@ protected function run($argument): void { [$data, $webhookId] = $argument; $webhookListener = $this->mapper->getById($webhookId); $client = $this->clientService->newClient(); + + // adding Ephemeral auth tokens to the call + $data['tokens'] = $this->tokenService->getTokens($webhookListener, $data['user']['uid'] ?? null); $options = [ 'verify' => $this->certificateManager->getAbsoluteBundlePath(), 'headers' => $webhookListener->getHeaders() ?? [], diff --git a/apps/webhook_listeners/lib/BackgroundJobs/WebhookTokenCleanup.php b/apps/webhook_listeners/lib/BackgroundJobs/WebhookTokenCleanup.php new file mode 100644 index 0000000000000..45f934333d065 --- /dev/null +++ b/apps/webhook_listeners/lib/BackgroundJobs/WebhookTokenCleanup.php @@ -0,0 +1,33 @@ +setInterval(5 * 60); + } + + /** + * @param array $argument + */ + protected function run($argument): void { + $this->tokenMapper->invalidateOldTokens(); + } +} diff --git a/apps/webhook_listeners/lib/Controller/WebhooksController.php b/apps/webhook_listeners/lib/Controller/WebhooksController.php index 97bc8aa875eb5..0eb6913f9d896 100644 --- a/apps/webhook_listeners/lib/Controller/WebhooksController.php +++ b/apps/webhook_listeners/lib/Controller/WebhooksController.php @@ -112,6 +112,11 @@ public function show(int $id): DataResponse { * @param ?array $headers Array of headers to send * @param "none"|"header"|null $authMethod Authentication method to use * @param ?array $authData Array of data for authentication + * @param ?array{user_ids?:list,user_roles?:list} $tokenNeeded + * List of user ids for which to include auth tokens in the event. + * Has two fields: "user_ids" list of user uids for which tokens are needed, "user_roles" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. + * Possible roles: "owner" for the user creating the webhook, "trigger" for the user triggering the webhook call. + * Requested auth tokens are valid for 1 hour after receiving them in the event call request. * * @return DataResponse * @@ -134,6 +139,7 @@ public function create( ?string $authMethod, #[\SensitiveParameter] ?array $authData, + ?array $tokenNeeded = null, ): DataResponse { $appId = null; if ($this->session->get('app_api') === true) { @@ -156,6 +162,7 @@ public function create( $headers, $authMethod, $authData, + $tokenNeeded, ); return new DataResponse($webhookListener->jsonSerialize()); } catch (\UnexpectedValueException $e) { @@ -180,6 +187,11 @@ public function create( * @param ?array $headers Array of headers to send * @param "none"|"header"|null $authMethod Authentication method to use * @param ?array $authData Array of data for authentication + * @param ?array{user_ids?:list,user_roles?:list} $tokenNeeded + * List of user ids for which to include auth tokens in the event. + * Has two fields: "user_ids" list of user uids for which tokens are needed, "user_roles" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. + * Possible roles: "owner" for the user creating the webhook, "trigger" for the user triggering the webhook call. + * Requested auth tokens are valid for 1 hour after receiving them in the event call request. * * @return DataResponse * @@ -203,6 +215,7 @@ public function update( ?string $authMethod, #[\SensitiveParameter] ?array $authData, + ?array $tokenNeeded = null, ): DataResponse { $appId = null; if ($this->session->get('app_api') === true) { @@ -226,6 +239,7 @@ public function update( $headers, $authMethod, $authData, + $tokenNeeded, ); return new DataResponse($webhookListener->jsonSerialize()); } catch (\UnexpectedValueException $e) { diff --git a/apps/webhook_listeners/lib/Db/EphemeralToken.php b/apps/webhook_listeners/lib/Db/EphemeralToken.php new file mode 100644 index 0000000000000..41aad2ba2a935 --- /dev/null +++ b/apps/webhook_listeners/lib/Db/EphemeralToken.php @@ -0,0 +1,54 @@ +addType('tokenId', 'integer'); + $this->addType('userId', 'string'); + $this->addType('createdAt', 'integer'); + } + + public function jsonSerialize(): array { + $fields = array_keys($this->getFieldTypes()); + return array_combine( + $fields, + array_map( + fn ($field) => $this->getter($field), + $fields + ) + ); + } +} diff --git a/apps/webhook_listeners/lib/Db/EphemeralTokenMapper.php b/apps/webhook_listeners/lib/Db/EphemeralTokenMapper.php new file mode 100644 index 0000000000000..9b5201096a44e --- /dev/null +++ b/apps/webhook_listeners/lib/Db/EphemeralTokenMapper.php @@ -0,0 +1,121 @@ + + */ + +class EphemeralTokenMapper extends QBMapper { + public const TABLE_NAME = 'webhook_tokens'; + public const TOKEN_LIFETIME = 1 * 1 * 60; // one hour in seconds + + public function __construct( + IDBConnection $db, + private LoggerInterface $logger, + private ITimeFactory $time, + private PublicKeyTokenMapper $tokenMapper, + ) { + parent::__construct($db, self::TABLE_NAME, EphemeralToken::class); + } + + /** + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + * @throws Exception + */ + public function getById(int $id): EphemeralToken { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + + return $this->findEntity($qb); + } + + /** + * @throws Exception + * @return EphemeralToken[] + */ + public function getAll(): array { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()); + + return $this->findEntities($qb); + } + + + /** + * @param int $olderThan + * @return EphemeralToken[] + * @throws Exception + */ + public function getOlderThan($olderThan): array { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where($qb->expr()->lt('created_at', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT))); + + return $this->findEntities($qb); + } + + /** + * @throws Exception + */ + public function addEphemeralToken( + int $tokenId, + ?string $userId, + int $createdAt, + ): EphemeralToken { + $tempToken = EphemeralToken::fromParams( + [ + 'tokenId' => $tokenId, + 'userId' => $userId, + 'createdAt' => $createdAt, + ] + ); + return $this->insert($tempToken); + } + public function invalidateOldTokens(int $token_lifetime = self::TOKEN_LIFETIME) { + $olderThan = $this->time->getTime() - $token_lifetime; + try { + $tokensToDelete = $this->getOlderThan($olderThan); + } catch (Exception $e) { + $this->logger->error('Webhook token deletion failed: ' . $e->getMessage(), ['exception' => $e]); + return; + } + + + $this->logger->debug('Invalidating ephemeral webhook tokens older than ' . date('c', $olderThan), ['app' => 'webhook_listeners']); + foreach ($tokensToDelete as $token) { + try { + $this->tokenMapper->delete($this->tokenMapper->getTokenById($token->getTokenId())); // delete token itself + $this->delete($token); // delete db row in webhook_tokens + } catch (Exception $e) { + $this->logger->error('Webhook token deletion failed: ' . $e->getMessage(), ['exception' => $e]); + } + + } + } +} diff --git a/apps/webhook_listeners/lib/Db/WebhookListener.php b/apps/webhook_listeners/lib/Db/WebhookListener.php index af974e7b0e25f..845624c87ad24 100644 --- a/apps/webhook_listeners/lib/Db/WebhookListener.php +++ b/apps/webhook_listeners/lib/Db/WebhookListener.php @@ -23,6 +23,7 @@ * @method ?string getAuthData() * @method void setAuthData(?string $data) * @method string getAuthMethod() + * @method ?array getTokenNeeded() * @psalm-suppress PropertyNotSetInConstructor */ class WebhookListener extends Entity implements \JsonSerializable { @@ -84,8 +85,15 @@ class WebhookListener extends Entity implements \JsonSerializable { */ protected $authData = null; + /** + * @var array + * @psalm-suppress PropertyNotSetInConstructor + */ + protected $tokenNeeded; + private ICrypto $crypto; + public function __construct( ?ICrypto $crypto = null, ) { @@ -103,6 +111,7 @@ public function __construct( $this->addType('headers', 'json'); $this->addType('authMethod', 'string'); $this->addType('authData', 'string'); + $this->addType('tokenNeeded', 'json'); } public function getAuthMethodEnum(): AuthMethod { diff --git a/apps/webhook_listeners/lib/Db/WebhookListenerMapper.php b/apps/webhook_listeners/lib/Db/WebhookListenerMapper.php index 75456cc0b7555..bef8223b31599 100644 --- a/apps/webhook_listeners/lib/Db/WebhookListenerMapper.php +++ b/apps/webhook_listeners/lib/Db/WebhookListenerMapper.php @@ -82,6 +82,7 @@ public function addWebhookListener( AuthMethod $authMethod, #[\SensitiveParameter] ?array $authData, + ?array $tokenNeeded = [], ): WebhookListener { /* Remove any superfluous antislash */ $event = ltrim($event, '\\'); @@ -99,6 +100,7 @@ public function addWebhookListener( 'userIdFilter' => $userIdFilter ?? '', 'headers' => $headers, 'authMethod' => $authMethod->value, + 'tokenNeeded' => $tokenNeeded ?? [], ] ); $webhookListener->setAuthDataClear($authData); @@ -122,6 +124,7 @@ public function updateWebhookListener( AuthMethod $authMethod, #[\SensitiveParameter] ?array $authData, + ?array $tokenNeeded = [], ): WebhookListener { /* Remove any superfluous antislash */ $event = ltrim($event, '\\'); @@ -140,6 +143,7 @@ public function updateWebhookListener( 'userIdFilter' => $userIdFilter ?? '', 'headers' => $headers, 'authMethod' => $authMethod->value, + 'tokenNeeded' => $tokenNeeded ?? [], ] ); $webhookListener->setAuthDataClear($authData); diff --git a/apps/webhook_listeners/lib/Migration/Version1500Date20251007130000.php b/apps/webhook_listeners/lib/Migration/Version1500Date20251007130000.php new file mode 100644 index 0000000000000..4ca578da98936 --- /dev/null +++ b/apps/webhook_listeners/lib/Migration/Version1500Date20251007130000.php @@ -0,0 +1,67 @@ +hasTable(WebhookListenerMapper::TABLE_NAME)) { + $table = $schema->getTable(WebhookListenerMapper::TABLE_NAME); + if (!$table->hasColumn('token_needed')) { + $schemaHasChanged = true; + $table->addColumn('token_needed', Types::TEXT, [ + 'notnull' => false, + ]); + } + } + + if (!$schema->hasTable(EphemeralTokenMapper::TABLE_NAME)) { + $schemaHasChanged = true; + $table = $schema->createTable(EphemeralTokenMapper::TABLE_NAME); + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('token_id', Types::BIGINT, [ + 'notnull' => true, + 'length' => 4, + 'unsigned' => true, + ]); + $table->addColumn('user_id', Types::STRING, [ + 'notnull' => false, + 'length' => 64, + ]); + $table->addColumn('created_at', Types::BIGINT, [ + 'notnull' => true, + 'length' => 4, + 'unsigned' => true, + ]); + $table->setPrimaryKey(['id']); + } + return $schemaHasChanged ? $schema : null; + } +} diff --git a/apps/webhook_listeners/lib/ResponseDefinitions.php b/apps/webhook_listeners/lib/ResponseDefinitions.php index 725e00b118ab6..b0c3850bb1bbd 100644 --- a/apps/webhook_listeners/lib/ResponseDefinitions.php +++ b/apps/webhook_listeners/lib/ResponseDefinitions.php @@ -21,6 +21,7 @@ * headers?: array, * authMethod: string, * authData?: array, + * tokenNeeded?: ?array{user_ids?:array,user_roles?:array}, * } */ class ResponseDefinitions { diff --git a/apps/webhook_listeners/lib/Service/TokenService.php b/apps/webhook_listeners/lib/Service/TokenService.php new file mode 100644 index 0000000000000..e75d1b8f4e006 --- /dev/null +++ b/apps/webhook_listeners/lib/Service/TokenService.php @@ -0,0 +1,122 @@ + ['jane', 'bob'], 'user_roles' => ['owner', 'trigger']] + * as requested tokens in the registered webhook produces a result like + * ['user_ids' => [['jane' => 'abcdtokenabcd1'], ['bob','=> 'abcdtokenabcd2']], 'user_roles' => ['owner' => ['admin' => 'abcdtokenabcd3'], 'trigger' => ['user1' => 'abcdtokenabcd4']]] + * Created auth tokens are valid for 1 hour. + * + * @param WebhookListener $webhookListener + * @param ?string $triggerUserId the user that triggered the webhook call + * @return array{user_ids?:array,user_roles?:array{owner?:array,trigger?:array}} + */ + public function getTokens(WebhookListener $webhookListener, ?string $triggerUserId): array { + $tokens = [ + 'user_ids' => [], + 'user_roles' => [], + ]; + $tokenNeeded = $webhookListener->getTokenNeeded(); + if (isset($tokenNeeded['user_ids'])) { + foreach ($tokenNeeded['user_ids'] as $userId) { + try { + $tokens['user_ids'][$userId] = $this->createEphemeralToken($userId); + } catch (\Exception $e) { + $this->logger->error('Webhook token creation for user ' . $userId . ' failed: ' . $e->getMessage(), ['exception' => $e]); + } + + } + } + if (isset($tokenNeeded['user_roles'])) { + foreach ($tokenNeeded['user_roles'] as $user_role) { + switch ($user_role) { + case 'owner': + // token for the person who created the flow + $ownerId = $webhookListener->getUserId(); + if (is_null($ownerId)) { // no owner uid available + break; + } + $tokens['user_roles']['owner'] = [ + $ownerId => $this->createEphemeralToken($ownerId) + ]; + break; + case 'trigger': + // token for the person who triggered the webhook + if (is_null($triggerUserId)) { // no trigger uid available + break; + } + $tokens['user_roles']['trigger'] = [ + $triggerUserId => $this->createEphemeralToken($triggerUserId) + ]; + break; + default: + $this->logger->error('Webhook token creation for user role ' . $user_role . ' not defined. ', ['Not defined' => $user_role]); + + } + } + } + return $tokens; + } + private function createEphemeralToken(string $userId): string { + $token = $this->generateRandomDeviceToken(); + + // we need the user`s language to have the token name showing up in the session list in the correct language + $user = $this->userManager->get($userId); + $lang = $this->l10nFactory->getUserLanguage($user); + $l = $this->l10nFactory->get('webhook_listeners', $lang); + $name = $l->t('Ephemeral webhook authentication'); + $password = null; + $deviceToken = $this->tokenProvider->generateToken( + $token, + $userId, + $userId, + $password, + $name, + IToken::PERMANENT_TOKEN); + + $this->tokenMapper->addEphemeralToken( + $deviceToken->getId(), + $userId, + $this->time->getTime()); + return $token; + } + + private function generateRandomDeviceToken(): string { + $groups = []; + for ($i = 0; $i < 5; $i++) { + $groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE); + } + return implode('-', $groups); + } +} diff --git a/apps/webhook_listeners/openapi.json b/apps/webhook_listeners/openapi.json index 36c80a43de84d..2a01c8bcb6885 100644 --- a/apps/webhook_listeners/openapi.json +++ b/apps/webhook_listeners/openapi.json @@ -92,6 +92,24 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "properties": { + "user_ids": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "user_roles": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } } } } @@ -304,6 +322,26 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "default": null, + "description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.", + "properties": { + "user_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_roles": { + "type": "array", + "items": { + "type": "string" + } + } + } } } } @@ -703,6 +741,26 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "default": null, + "description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.", + "properties": { + "user_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_roles": { + "type": "array", + "items": { + "type": "string" + } + } + } } } } diff --git a/openapi.json b/openapi.json index 4307c29839e24..592bc2657acbd 100644 --- a/openapi.json +++ b/openapi.json @@ -4687,6 +4687,24 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "properties": { + "user_ids": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "user_roles": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } } } } @@ -38254,6 +38272,26 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "default": null, + "description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.", + "properties": { + "user_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_roles": { + "type": "array", + "items": { + "type": "string" + } + } + } } } } @@ -38653,6 +38691,26 @@ "additionalProperties": { "type": "object" } + }, + "tokenNeeded": { + "type": "object", + "nullable": true, + "default": null, + "description": "List of user ids for which to include auth tokens in the event. Has two fields: \"user_ids\" list of user uids for which tokens are needed, \"user_roles\" list of roles (users not defined by their ID but by the role they have in the webhook event) for which tokens can be included. Possible roles: \"owner\" for the user creating the webhook, \"trigger\" for the user triggering the webhook call. Requested auth tokens are valid for 1 hour after receiving them in the event call request.", + "properties": { + "user_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_roles": { + "type": "array", + "items": { + "type": "string" + } + } + } } } }