-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Feat(webhook_listeners): add auth tokens to webhook call #55790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
563aaf5
feat: add token array to webhook definition and db entries
janepie db158ce
feat: add tokens in the webhook call data
janepie 2daff2d
fix: Apply suggestions from code review
janepie be9b246
fix: handling of unavailable uids
janepie 5689d94
feat: token deletion logic
janepie 5ad1ea3
fix: rename field names of tokenNeeded
janepie e232b48
fix: add default to tokenNeeded
janepie 134943e
feat: add information about token validity times
janepie eafb602
fix: fix psalm
janepie 3296842
chore: bump version nr
janepie 75f7bed
refactor: use constants for db column types
janepie 5e1f2d2
fix: translate token name
janepie 4dab621
fix: include review feedback
janepie b02966a
fix: remove redundant db column
janepie b364542
fix: regenerate openapi.json
janepie 6c855d0
feat: error handling
janepie 694ecce
fix: use delete method instead of deleteByTokenId
janepie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
apps/webhook_listeners/lib/BackgroundJobs/WebhookTokenCleanup.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors | ||
| * SPDX-License-Identifier: AGPL-3.0-or-later | ||
| */ | ||
|
|
||
| namespace OCA\WebhookListeners\BackgroundJobs; | ||
|
|
||
| use OCA\WebhookListeners\Db\EphemeralTokenMapper; | ||
| use OCP\AppFramework\Utility\ITimeFactory; | ||
| use OCP\BackgroundJob\TimedJob; | ||
|
|
||
| class WebhookTokenCleanup extends TimedJob { | ||
|
|
||
| public function __construct( | ||
| private EphemeralTokenMapper $tokenMapper, | ||
| ITimeFactory $timeFactory, | ||
| ) { | ||
| parent::__construct($timeFactory); | ||
| // every 5 min | ||
| $this->setInterval(5 * 60); | ||
| } | ||
|
|
||
| /** | ||
| * @param array $argument | ||
| */ | ||
| protected function run($argument): void { | ||
| $this->tokenMapper->invalidateOldTokens(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors | ||
| * SPDX-License-Identifier: AGPL-3.0-or-later | ||
| */ | ||
|
|
||
| namespace OCA\WebhookListeners\Db; | ||
|
|
||
| use OCP\AppFramework\Db\Entity; | ||
|
|
||
| /** | ||
| * @method int getTokenId() | ||
| * @method ?string getUserId() | ||
| * @method int getCreatedAt() | ||
| * @psalm-suppress PropertyNotSetInConstructor | ||
| */ | ||
| class EphemeralToken extends Entity implements \JsonSerializable { | ||
| /** | ||
| * @var int id of the token in the oc_authtoken db table | ||
| */ | ||
| protected $tokenId; | ||
|
|
||
| /** | ||
| * @var ?string id of the user wich the token belongs to | ||
| * @psalm-suppress PropertyNotSetInConstructor | ||
| */ | ||
| protected $userId = null; | ||
|
|
||
| /** | ||
| * @var int token creation timestamp | ||
| * @psalm-suppress PropertyNotSetInConstructor | ||
| */ | ||
| protected $createdAt; | ||
|
|
||
| public function __construct() { | ||
| $this->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 | ||
| ) | ||
| ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors | ||
| * SPDX-License-Identifier: AGPL-3.0-or-later | ||
| */ | ||
|
|
||
| namespace OCA\WebhookListeners\Db; | ||
|
|
||
| use OC\Authentication\Token\PublicKeyTokenMapper; | ||
| use OCP\AppFramework\Db\DoesNotExistException; | ||
| use OCP\AppFramework\Db\MultipleObjectsReturnedException; | ||
| use OCP\AppFramework\Db\QBMapper; | ||
| use OCP\AppFramework\Utility\ITimeFactory; | ||
| use OCP\DB\Exception; | ||
| use OCP\DB\QueryBuilder\IQueryBuilder; | ||
| use OCP\IDBConnection; | ||
| use Psr\Log\LoggerInterface; | ||
|
|
||
| /** | ||
| * @template-extends QBMapper<EphemeralToken> | ||
| */ | ||
|
|
||
| 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]); | ||
janepie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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]); | ||
| } | ||
|
|
||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.