-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
base: master
Are you sure you want to change the base?
Conversation
e804cd1 to
5b188b6
Compare
julien-nc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From you branch, at the top of server, you can run
composer i
# you might wanna revert what it does to lib/composer
git checkout ./lib/composer/
composer run cs:check ./apps/webhook_listeners/lib
# or if you have Php 8.4 like me and it's refused by php-cs
PHP_CS_FIXER_IGNORE_ENV=1 composer run cs:check ./apps/webhook_listeners/lib
# and if you want to fix the syntax issues:
composer run cs:fix ./apps/webhook_listeners/lib(for later) To implement the token expiration mechanism you might need a new table where you store
- the token ID
- the webhook ID
- if needed, the token creation timestamp
33585c0 to
78ea408
Compare
78ea408 to
8dae570
Compare
8dae570 to
12747b1
Compare
12747b1 to
8572f9a
Compare
|
Who will revoke generated token once it is not needed? Will we revoke it in the server repo or in the Windmill integration app itself? Another question related to previous: If the token was generated for user |
IMO the tokens should be revoked by a background job in webhook_listeners.
I think it's clearer and cleaner if we have independent tokens for each webhook "call". If the token generated for the first run leaks for some reason, it's bad if we make it live longer. |
This definetly should be added as a comment to the code. |
|
Yep, this PR is still WIP. This is planned. |
0c0a98a to
7847d3b
Compare
2d030cc to
f53ad98
Compare
647a122 to
a6c77a7
Compare
0ff523e to
954d099
Compare
No worries, thanks for reviewing so thoroughly! I think I got everything |
| $this->logger->debug('Invalidating ephemeral webhook tokens older than ' . date('c', $olderThan), ['app' => 'webhook_listeners']); | ||
| foreach ($tokensToDelete as $token) { | ||
| $this->tokenMapper->invalidate($this->tokenMapper->getTokenById($token->getTokenId())->getToken()); // delete token itself | ||
| $this->deleteByTokenId($token->getTokenId()); // delete db row in webhook_tokens |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error handling is missing here, I think. What if the token doesn't exist anymore in either table? The other tokens should still be deleted. Also, the getOlderThan could fail, I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm if it's failing to delete in the authtokens table, should it stay in the second table or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getOlderThan is a db query, what kind of failing are you thinking of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Query failure. All db queries can fail with a DB Exception. That should always be handled, IMO
7f2528e to
0d081b6
Compare
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
Signed-off-by: Jana Peper <[email protected]>
3669f4c to
c45c268
Compare
| $tokensToDelete = $this->getOlderThan($olderThan); | ||
| } | ||
| catch(Exception $e){ | ||
| $this->logger->error('Webhook token deletion failed: ' . $e->getMessage(), ['exception' => $e]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return, please :)
959d97d to
be08ce8
Compare
| $client = $this->clientService->newClient(); | ||
|
|
||
| // adding Ephemeral auth tokens to the call | ||
| $data['tokens'] = $this->tokenService->getTokens($webhookListener, $data['user']['uid']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$data['user'] may be null if an event is triggered from a public page.
| $data['tokens'] = $this->tokenService->getTokens($webhookListener, $data['user']['uid']); | |
| $data['tokens'] = $this->tokenService->getTokens($webhookListener, $data['user']['uid'] ?? null); |
Something like that would work, if getTokens supports getting null as second parameter.
| $this->logger->debug('Invalidating ephemeral webhook tokens older than ' . date('c', $olderThan), ['app' => 'webhook_listeners']); | ||
| foreach ($tokensToDelete as $token) { | ||
| try { | ||
| $this->tokenMapper->invalidate($this->tokenMapper->getTokenById($token->getTokenId())->getToken()); // delete token itself |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| $this->tokenMapper->invalidate($this->tokenMapper->getTokenById($token->getTokenId())->getToken()); // delete token itself | |
| $this->tokenMapper->delete($this->tokenMapper->getTokenById($token->getTokenId())); // delete token itself |
| ]; | ||
| break; | ||
| default: | ||
| $this->logger->error('Webhook token creation for user role ' . $function . ' not defined. ' . $e->getMessage(), ['exception' => $e]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no $e object in this context or did I miss something?
| } | ||
| } | ||
| if (isset($tokenNeeded['user_roles'])) { | ||
| foreach ($tokenNeeded['user_roles'] as $function) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| foreach ($tokenNeeded['user_roles'] as $function) { | |
| foreach ($tokenNeeded['user_roles'] as $user_role) { |
The namings $function and $functionId in this loop are quite confusing.
| // We need the getToken() method to be able to send the token out. | ||
| // That method is only available in PublicKeyToken which is returned by generateToken | ||
| // but not declared as such, so we have to check the type here | ||
| if (!($deviceToken instanceof PublicKeyToken)) { // type needed for the getToken() function | ||
| throw new \Exception('Unexpected token type'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // We need the getToken() method to be able to send the token out. | |
| // That method is only available in PublicKeyToken which is returned by generateToken | |
| // but not declared as such, so we have to check the type here | |
| if (!($deviceToken instanceof PublicKeyToken)) { // type needed for the getToken() function | |
| throw new \Exception('Unexpected token type'); | |
| } |
This is not true anymore, only getId is used and it’s part of the public interface.
Signed-off-by: Jana Peper <[email protected]>
be08ce8 to
a38ab95
Compare
Summary
Needed for Windmill integration.
This code adds an option to ask for authentication tokens when registering a webhook. The requested tokens will be added to the dispatched request to the defined endpoint.
All tokens generated with this have a lifetime of 1 hour and will be deleted after that in a background job every 5 min.
Tokens can be requested in the tokenNeeded parameter, which accepts listed values in the two fields "user_ids" and "user_roles".
"user_ids" is a list of user uids for which tokens are needed, "user_roles" is a 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.
Checklist
3. to review, feature component)stable32)