-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathChangelog
426 lines (272 loc) · 12.2 KB
/
Changelog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
===========
Changelog
===========
.. _version-1.5.0:
1.5.0
=====
:release-date: 2016-10-20 11:08 A.M PDT
:release-by: Ask Solem
- New API for :class:`~thorn.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:
.. code-block:: python
@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 :ref:`event-buffering` for more information.
- Models can now define additional headers to be passed on to webhooks.
See :ref:`events-model-header`.
Contributed by **Flavio Curella**.
- Django: :class:`~thorn.ModelEvent` now takes advantage of
``Model.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):
.. code-block:: python
from django.urls import reverse
class Article(models.Model):
def get_absolute_url(self):
return reverse('article:detail', kwargs={'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 :setting:`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:
.. code-block:: python
ModelEvent(..., signal_honors_transaction=True)
Disabled by default.
To be enabled by default in Thorn 2.0.
- :class:`~thorn.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
.. _version-1.4.2:
1.4.2
=====
:release-date: 2016-07-28 11:45 A.M PDT
- Serialize uuid as string when calling Celery tasks.
.. _version-1.4.1:
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.
.. _version-1.4.0:
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 :setting:`THORN_SUBSCRIBER_MODEL`
setting.
- Fixed HMAC signing wrong message value.
.. _version-1.3.0:
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: text
“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:
.. code-block:: python
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 :setting:`THORN_SUBSCRIBER_MODEL` setting.
- New :setting:`THORN_HMAC_SIGNER` setting.
- Requirements: Tests now depends on :pypi:`case` 1.2.2
- JSON: Make sure simplejson does not convert :class:`~decimal.Decimal`
to :class:`float`.
- 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:
.. code-block:: python
on_created=ModelEvent('created.{.occasion}')
means the format will expand into ``instance.occasion``.
Subclasses of :class:`~thorn.events.ModelEvent` can override how the name is
expanded by defining the ``_get_name`` method.
.. _version-1.2.1:
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.
.. _version-1.2.0:
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 :setting:`THORN_SUBSCRIBERS`
setting.
- Decorator: ``model.webhook_events`` is now a UserDict proxy
to ``model.webhook_events.events``.
- Subscriber: :class:`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 :func:`isinstance` checks.
- Q: ``now__*`` operators now properly handles the case when there's
no previous version of the object.
- Django: :data:`django.db.models.signals.pre_save` signal handler
now ignores :exc:`~Adjango.core.exceptions.ObjectDoesNotExist` 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 to :mod:`thorn.generic.models`.
.. _version-1.1.0:
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
- :pypi:`itsdangerous`
- :pypi:`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 :ref:`Django guide <django-example-consumer>`.
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`` and ``127.0.0.1``
This behavior can be changed globally using the
:setting:`THORN_RECIPIENT_VALIDATORS` setting, or on an per-event basis
using the ``recipient_validators`` argument to :class:`~Thorn.event`.
- Security: Now only dispatches to HTTP and HTTPS URLs by default.
This behavior can be changed globally using the
:setting:`THORN_RECIPIENT_VALIDATORS` setting, or on an per-event basis
using the ``recipient_validators`` argument to :class:`~Thorn.event`.
- Security: Now only dispatches to ports 80 and 443 by default.
This behavior can be changed globally using the
:setting:`THORN_RECIPIENT_VALIDATORS` setting, or on an per-event basis
using the ``recipient_validators`` argument to :class:`~Thorn.event`.
- Security: Adds recipient validators
You can now validate the recipient URL by providing a list
of validators in the ``recipient_validators`` argument to
:class:`~thorn.Event`.
The default list of validators is provided by the
new :setting:`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:
.. code-block:: python
@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
.. _version-1.0.0:
1.0.0
=====
:release-date: 2016-05-13 10:10 A.M PDT
:release-by: Ask Solem
- Initial release :o)