Changelog¶
1.5.0¶
release-date: | 2016-10-20 11:08 A.M PDT |
---|---|
release-by: | Ask Solem |
New API for
ModelEvent
.After having used Thorn for a while, there was a realization that passing lots of arguments to a decorator looks very messy when there are many events for a model.
We have come up with a new way to add webhooks to models, that we believe is more tidy.
The new API moves declaration of webhooks related things into a nested class:
@webhook_model class Article(models.Model): uuid = models.UUIDField() title = models.CharField(max_length=128) state = models.CharField(max_length=128, default='PENDING') body = models.TextField() user = models.ForeignKey(settings.AUTH_USER_MODEL) class webhooks: on_create = ModelEvent('article.created') on_change = ModelEvent('article.changed') on_delete = ModelEvent('article.removed') on_publish = ModelEvent( 'article.published', state__now_eq='PUBLISHED', ).dispatches_on_change() def payload(self, article): return { 'title': article.title, } def headers(self, article): return { 'Authorization': 'Bearer {}'.format(article.user.access_token), }
Note
The old API is still supported, and there’s no current plans of deprecating the arguments to the decorator itself.
Adds support for buffering events - moving dispatch out of signal handlers.
See Buffering for more information.
Models can now define additional headers to be passed on to webhooks.
Contributed by Flavio Curella.
Django:
ModelEvent
now takes advantage ofModel.get_absolute_url()
.Instead of defining a reverser you can now simply have your model define a
get_absolute_url
method (which is a convention already):class Article(models.Model): @models.permalink def get_absolute_url(self): return 'article:detail', (), {'slug': self.slug}
For more information on defining this method, refer to the Django documentation: https://docs.djangoproject.com/en/stable/ref/models/instances/#get-absolute-url
New
THORN_SIGNAL_HONORS_TRANSACTION
setting.If enabled the Django model events will dispatch only after the transaction is committed, and should the transaction be rolled back the events are discarded.
You can also enable this setting on individual events:
ModelEvent(..., signal_honors_transaction=True)
Disabled by default.
To be enabled by default in Thorn 2.0.
ModelEvent
now logs errors instead of propagating them.ModelEvent(propagate_errors=True)
may be set to revert to the old behavior.URLs now ordered by scheme,host,port (was host,port,scheme).
Documentation improvements by:
- Flavio Curella
1.4.1¶
release-date: | 2016-07-26 01:06 P.M PDT |
---|---|
release-by: | Ask Solem |
- Fixed webhook dispatch crash where validator was deserialized twice.
- Celery dispatcher did not properly forward the Hook-Subscription HTTP header.
- Source code now using Google-style docstrings.
1.4.0¶
release-date: | 2016-07-11 04:27 P.M PDT |
---|---|
release-by: | Ask Solem |
New HTTP header now sent with events:
Hook-Subscription
This contains the UUID of the subscription, which means a webhook consumer may now react by cancelling or modifying the subscription.
Fixed missing default value for the
THORN_SUBSCRIBER_MODEL
setting.Fixed HMAC signing wrong message value.
1.3.0¶
release-date: | 2016-07-07 07:40 P.M PDT |
---|---|
release-by: | Ask Solem |
New and improved method for HMAC signing.
The new method must be enabled manually by setting:
THORN_HMAC_SIGNER = 'thorn.utils.hmac:sign'
It turns out itsdangerous did not do what we expected it to, instead it does this:
The secret key is transformed into:
key = hashlib.sha256(salt + 'signer' + HMAC_SECRET) # strip = from beginning and end of the base64 string key = key.strip('=')
If you don’t specify a salt, which we don’t, there is a default salt(!) which is:
“itsdangerous.Signer”
The extra “signer” in the key transformation is there as the default key_derivation method is called “django-concat”.
The final signature is encoded using “urlsafe_b64encode”
So in Python to recreate the signature using the built-in hmac library you would have to do:
import hashlib import hmac from base64 import urlsafe_b64encode # everything hardcoded to SHA256 here def create_signature(secret_key, message): key = hashlib.sha256( 'itsdangerous.Signer' + 'signer' + secret_key).digest() digest = hmac.new(key, message, digestmod=hashlib.sha256).digest() return urlsafe_b64encode(digest).replace('=')
which is much more complicated than what we can expect of users.
You’re highly encouraged to enable the new HMAC method, but sadly it’s not backwards compatible.
We have also included new examples for verifying HMAC signatures in Django, Ruby, and PHP in the documentation.
New
THORN_SUBSCRIBER_MODEL
setting.New
THORN_HMAC_SIGNER
setting.Requirements: Tests now depends on case 1.2.2
JSON: Make sure simplejson does not convert
Decimal
tofloat
.class:~thorn.events.ModelEvent: name can now be a string format.
Contributed by Flavio Curella.
The format expands using the model instance affected, e.g:
on_created=ModelEvent('created.{.occasion}')
means the format will expand into
instance.occasion
.Subclasses of
ModelEvent
can override how the name is expanded by defining the_get_name
method.
1.2.1¶
release-date: | 2016-06-06 06:30 P.M PDT |
---|---|
release-by: | Ask Solem |
- Celery: Forward event signal context to the tasks.
1.2.0¶
release-date: | 2016-06-02 01:00 P.M PDT |
---|---|
release-by: | Ask Solem |
Event: Adds
request_data
option.This enables you to inject additional data into the webhook payload, used for integration with quirky HTTP endpoints.
Event: Adds
allow_keepalive
option.HTTP connections will not be reused for an event if this flag is set to False. Keepalive is enabled by default.
Event: Adds
subscribers
argument that can be used to add default subscribers for the event.This argument can hold the same values as the
THORN_SUBSCRIBERS
setting.- Decorator:
model.webhook_events
is now a UserDict proxy to
model.webhook_events.events
.
- Decorator:
Subscriber:
thorn.generic.models.AbstractSubscribers
is a new abstract interface for subscriber models.This should be used if you want to check if an object is a subscriber in
isinstance()
checks.- Q:
now__*
operators now properly handles the case when there’s no previous version of the object.
- Q:
Django:
django.db.models.signals.pre_save
signal handler now ignoresObjectDoesNotExist
errors.Events: Adds new
prepare_recipient_validators
method, enabling subclasses to e.g. set default validators.Windows: Unit test suite now passing on win32/win64.
Module
thorn.models
renamed tothorn.generic.models
.
1.1.0¶
release-date: | 2016-05-23 12:00 P.M PDT |
---|---|
release-by: | Ask Solem |
Fixed installation on Python 3
Fix contributed by Josh Drake.
Now depends on
- itsdangerous
- ipaddress (Python 2.7)
Security: Now provides HMAC signing by default.
The Subscriber model has a new
hmac_secret
field which subscribers can provide to set the secret key for communication. A default secret will be created if none is provided, and can be found in the response of the subscribe endpoint.The signed HMAC message found in the
Hook-HMAC
HTTP header can then be used to verify the sender of the webhook.An example Django webhook consumer verifying the signature can be found in the Django guide.
Thanks to Timothy Fitz for suggestions.
Security: No longer dispatches webhooks to internal networks.
This means Thorn will refuse to deliver webhooks to networks considered internal, like
fd00::/8
,10.0.0.0/8
,172.16.0.0/12
,192.168.0.0/16
and127.0.0.1
This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORS
setting, or on an per-event basis using therecipient_validators
argument toevent
.Security: Now only dispatches to HTTP and HTTPS URLs by default.
This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORS
setting, or on an per-event basis using therecipient_validators
argument toevent
.Security: Now only dispatches to ports 80 and 443 by default.
This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORS
setting, or on an per-event basis using therecipient_validators
argument toevent
.Security: Adds recipient validators
You can now validate the recipient URL by providing a list of validators in the
recipient_validators
argument toEvent
.The default list of validators is provided by the new
THORN_RECIPIENT_VALIDATORS
setting.Thanks to Edmond Wong for reviewing, and Timothy Fitz for suggestions.
Django: Now properly supports custom user models by using
UserModel.get_username()
.Fix contributed by Josh Drake.
ModelEvent: Adds new many-to-many signal dispatcher types
dispatches_on_m2m_add(related_field)
Sent when a new object is added to a many-to-many relation.
dispatches_on_m2m_remove(related_field)
Sent when an object is removed from a many-to-many relation.
dispatches_on_m2m_clear(related_field)
Sent when a many-to-many relation is cleared.
Example
In this blog article model, events are sent whenever a new tag is added or removed:
@webhook_model( on_add_tag=ModelEvent( 'article.tagged').dispatches_on_m2m_add('tags'), on_remove_tag=ModelEvent( 'article.untagged').dispatches_on_m2m_remove('tags'), on_clear_tags=ModelEvent( 'article.tags_cleared').dispatches_on_m2m_clear('tags'), ) class Article(models.Model): title = models.CharField(max_length=128) tags = models.ManyToManyField(Tag) class Tag(models.Model): name = models.CharField(max_length=64, unique=True)
The
article.tagged
webhook is sent when:>>> python_tag, _ = Tag.objects.get_or_create(name='python') >>> article.tags.add(python_tag) # <-- dispatches with this line
and the
article.untagged
webhook is sent when:>>> article.tags.remove(python_tag)
finally, the
article.tags_cleared
event is sent when:>>> article.tags.clear()
Documentation fixes contributed by:
- Matthew Brener