Changelog¶
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_MODELsetting.New
THORN_HMAC_SIGNERsetting.Requirements: Tests now depends on case 1.2.2
JSON: Make sure simplejson does not convert
Decimaltofloat.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
ModelEventcan override how the name is expanded by defining the_get_namemethod.
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_dataoption.This enables you to inject additional data into the webhook payload, used for integration with quirky HTTP endpoints.
Event: Adds
allow_keepaliveoption.HTTP connections will not be reused for an event if this flag is set to False. Keepalive is enabled by default.
Event: Adds
subscribersargument that can be used to add default subscribers for the event.This argument can hold the same values as the
THORN_SUBSCRIBERSsetting.- Decorator:
model.webhook_eventsis now a UserDict proxy to
model.webhook_events.events.
- Decorator:
Subscriber:
thorn.generic.models.AbstractSubscribersis 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_savesignal handler now ignoresObjectDoesNotExisterrors.Events: Adds new
prepare_recipient_validatorsmethod, enabling subclasses to e.g. set default validators.Windows: Unit test suite now passing on win32/win64.
Module
thorn.modelsrenamed 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_secretfield 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-HMACHTTP 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/16and127.0.0.1This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORSsetting, or on an per-event basis using therecipient_validatorsargument toevent.Security: Now only dispatches to HTTP and HTTPS URLs by default.
This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORSsetting, or on an per-event basis using therecipient_validatorsargument toevent.Security: Now only dispatches to ports 80 and 443 by default.
This behavior can be changed globally using the
THORN_RECIPIENT_VALIDATORSsetting, or on an per-event basis using therecipient_validatorsargument toevent.Security: Adds recipient validators
You can now validate the recipient URL by providing a list of validators in the
recipient_validatorsargument toEvent.The default list of validators is provided by the new
THORN_RECIPIENT_VALIDATORSsetting.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.taggedwebhook is sent when:>>> python_tag, _ = Tag.objects.get_or_create(name='python') >>> article.tags.add(python_tag) # <-- dispatches with this line
and the
article.untaggedwebhook is sent when:>>> article.tags.remove(python_tag)
finally, the
article.tags_clearedevent is sent when:>>> article.tags.clear()
Documentation fixes contributed by:
- Matthew Brener