-
Notifications
You must be signed in to change notification settings - Fork 50
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
Implement webhook plugin #104
base: main
Are you sure you want to change the base?
Conversation
Maybe it should be intergrated into the web_api plugin (or the other way round?) and the callback paths can be set from the outside that way? |
As a general comment, spurred by this code, not targeting this code specifically, and remembering that I'm just a customer, not even a committer here, I am hoping to get some clarity from @decentjohn around a policy for third-party plugins as to how they are hosted and delivered. On a more specific note, I agree with @Mimoja that unification of various, related services is a good idea. I think the existing plugin would benefit from a going over around security. One open port is, for some, already one port too many. |
Yes good question. I wasn’t really sure what the contribution policy was for third party plugins. Happy to take a look at the web_api plugin and see if it makes sense to merge in there, though it may actually be spiritually closer to the visualizer upload. Also I don’t really mind if this never gets merged in. It’s probably somewhat bespoke. |
I'm ok with any open port that defaults to localhost. To change it to an external IP, the user has to do something, and thus they take on that liability. That's a fairly common scheme I've seen. So.. bind() needs to be set to 127.0.0.1 as a default for anything that takes communication from the outside. |
array set ::plugins::webhook::settings { | ||
webhook_domain "example.com" | ||
webhook_endpoint "/decent/webhook" | ||
webhook_secret_key "SecretKeyForSigning" |
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.
You can't have a default password on a device that is sold in California unless it is device specific.
ref: https://leginfo.legislature.ca.gov/faces/billTextClient.xhtml?bill_id=201720180SB327
Edit: This may not be an incoming password, but a signing secret.
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.
Yes, this is just a signing secret for an outbound request. The signature is then attached as a header to the outbound request.
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.
why not make the password blank, and required to be created by the user before the plugin will work?
msg "triggering webhook" | ||
borg toast [translate "Triggering Webhook"] | ||
|
||
http::register https 443 [list ::tls::socket -servername $settings(webhook_domain)] |
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.
The docs might want to mention how to install a server certificate
Getting into how to generate one can probably be skipped, with "search for 'self-signed certificate'" or something appropriate.
If this is just sending POSTs, why is it opening a socket?
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 may be my misunderstanding, but I don’t believe this is binding anything/ opening a socket for listening. Based on my read of the docs, it’s used to establish a TLS transport for making the HTTPS request to the server: https://www.tcl-lang.org/man/tcl/TclCmd/http.htm#M49
https://wiki.tcl-lang.org/page/HTTPS
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.
yes that is correct, there is no listen there, that code is registering essentially an 'extension' to a socket. This is how https GET is implemented on tcl.
de1plus/plugins/webhook/plugin.tcl
Outdated
http::register https 443 [list ::tls::socket -servername $settings(webhook_domain)] | ||
|
||
# Craft HTTP Body | ||
set espresso_data [format_espresso_for_history] |
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 can take over a second to generate on a v1.3 stock tablet with a long shot. I've seen scale timeouts at one second with just the core app and things like the flow-cal profile. Though you've got this on the idle queue and after a delay, it can cause the BLE stack to back up and potentially lose packets.
This would have it running potentially three times; core app, visualiser upload, and web hook
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.
Good call. For my particular use case, I don’t actually require the history, so I’m fine with actually removing it unless someone finds it useful.
^ clock ^ HMAC signature | ||
``` | ||
|
||
Where `1617092331` represents the clock at the time of the shot, and `f728a8092b6882749efa61a6609414d14c6f25286bdb1fd76e0dda4bab0f0cdf` is the calculated HMAC signature. Note that they are delimited by the character `!`. |
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.
...clock at the time the payload generation began...
Why something predictable here? If this is just confirming that the sender and the receiver share the same secret, why not just a random number?
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.
Clock is used as a way to have a time-based component to the signature to mitigate replay attacks (really probably overkill for this). On the server receiving the request, it can verify that the timestamp is within some interval of the server time.
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.
OK, reasoning makes sense to me now
proc post_shot_data {} { | ||
variable settings | ||
|
||
msg "triggering webhook" |
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.
On msg
, please set severity codes. We're working on getting the logs down to the point where they can be meaningfully run by everyone, all the time, especially to help out the support team.
See https://github.com/decentespresso/de1app/blob/main/de1plus/logging.tcl#L28
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.
Will do
after [expr 1000 * $::plugins::webhook::settings(webhook_trigger_after)] ::plugins::webhook::post_shot_data | ||
} | ||
|
||
proc msg { msg } { |
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.
You can go straight to ::logging::default_logger
https://github.com/decentespresso/de1app/blob/main/de1plus/logging.tcl#L6
borg toast "Webhook succeeded!" | ||
} | ||
|
||
proc async_dispatch {old new} { |
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.
The {old new}
arg list was from the previous system that only triggered on major state changes.
As you're not doing anything with the states (yet), {event_dict}
is what the event will be called with.
|
||
proc main {} { | ||
::de1::event::listener::after_flow_complete_add \ | ||
[lambda {event_dict} { |
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.
See note above at Line 113
If your callback accepts {event_dict}
the wrapper for old-style callbacks is not needed.
} | ||
|
||
proc main {} { | ||
::de1::event::listener::after_flow_complete_add \ |
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.
package require wibble
??
Definitely should not be in the outer body or file, as it causes loading of the library even if the plugin is disabled.
There's some complexity here, looking back at #73
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.
Is this comment meant for another PR?
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.
I had thought you were opening a server, which I think people do with the wibble library. Though installed with AndroWish, it isn't part of the stock distro. ::http
is shown as part of the standard distro, so you don't need wibble.
Please don't take the comments as a lack of appreciation for the work! I'm picky. If I didn't think the concept was worth pursuing, I wouldn't have taken the time. |
Definitely, if you're doing a API, use ::http not wibble. Simpler is better. |
Provides the following settings: webhook_domain webhook_endpoint webhook_secret_key webhook_trigger_after Sends an HTTP webhook to: ``` POST https://$webhook_domain$webhook_endpoint Authorization: $clock!$hmac_signature $body ``` The HMAC signature is used so your server can verify the authenticity of a message. The signature is created from: `HMAC($webhook_secret_key, "$path\n$clock\n$body")`
d3bfc76
to
382be38
Compare
382be38
to
7b81514
Compare
This plugin provides functionality to trigger a webhook after a shot has completed.
Provides the following settings:
webhook_domain
webhook_endpoint
webhook_secret_key
webhook_trigger_after
Sends an HTTP webhook to:
The HMAC signature is used so your server can verify the authenticity of a
message. The signature is created from:
HMAC($webhook_secret_key, "$path\n$clock\n$body")