Implement OAuth2 provider (#927)

- [x] Application management
  - [x] Link
  - [ ] Tests
- [x] Authorize flow
  - [x] Tests
- [x] Refresh token handling
  - [x] Tests
- [x] Revocation endpoint
  - [x] Tests
  - [x] Mitigate: https://github.com/jazzband/django-oauth-toolkit/issues/585
- [x] API authenticator / permission driver
  - [x] Test
- [x] Enforce organizer restriction
  - [x] Tests
- [x] Enforce scope restriction
  - [x] Tests
- [x] Show current applications to user
  - [x] Revoke
  - [x] Tests
- [x] Log new authorizations
  - [x] notify user
- [x] Ensure other grant types are not available
- [x] Documentation
- [x] check if revoking access toking, then refreshing gets rid of organizer constraint
- [x] Show logentry foo
This commit is contained in:
Raphael Michel
2018-06-05 12:58:04 +02:00
committed by GitHub
parent df031b2222
commit 69d10489b8
53 changed files with 1786 additions and 116 deletions

View File

@@ -16,6 +16,7 @@ from django.utils.formats import date_format
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from pretix.api.models import OAuthApplication
from pretix.base.i18n import (
LazyCurrencyNumber, LazyDate, LazyLocaleException, LazyNumber, language,
)
@@ -80,7 +81,7 @@ logger = logging.getLogger(__name__)
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
force: bool=False, send_mail: bool=True, user: User=None, mail_text='',
count_waitinglist=True, api_token=None) -> Order:
count_waitinglist=True, auth=None) -> Order:
"""
Marks an order as paid. This sets the payment provider, info and date and returns
the order object.
@@ -123,7 +124,7 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
'date': date or now_dt,
'manual': manual,
'force': force
}, user=user, api_token=api_token)
}, user=user, auth=auth)
order_paid.send(order.event, order=order)
invoice = None
@@ -173,7 +174,7 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
return order
def extend_order(order: Order, new_date: datetime, force: bool=False, user: User=None, api_token=None):
def extend_order(order: Order, new_date: datetime, force: bool=False, user: User=None, auth=None):
"""
Extends the deadline of an order. If the order is already expired, the quota will be checked to
see if this is actually still possible. If ``force`` is set to ``True``, the result of this check
@@ -187,7 +188,7 @@ def extend_order(order: Order, new_date: datetime, force: bool=False, user: User
order.log_action(
'pretix.event.order.expirychanged',
user=user,
api_token=api_token,
auth=auth,
data={
'expires': order.expires,
'state_change': False
@@ -203,7 +204,7 @@ def extend_order(order: Order, new_date: datetime, force: bool=False, user: User
order.log_action(
'pretix.event.order.expirychanged',
user=user,
api_token=api_token,
auth=auth,
data={
'expires': order.expires,
'state_change': True
@@ -237,24 +238,21 @@ def mark_order_refunded(order, user=None, api_token=None):
@transaction.atomic
def mark_order_expired(order, user=None, api_token=None):
def mark_order_expired(order, user=None, auth=None):
"""
Mark this order as expired. This sets the payment status and returns the order object.
:param order: The order to change
:param user: The user that performed the change
:param api_token: The API token used to performed the change
"""
if isinstance(order, int):
order = Order.objects.get(pk=order)
if isinstance(user, int):
user = User.objects.get(pk=user)
if isinstance(api_token, int):
api_token = TeamAPIToken.objects.get(pk=api_token)
with order.event.lock():
order.status = Order.STATUS_EXPIRED
order.save()
order.log_action('pretix.event.order.expired', user=user, api_token=api_token)
order.log_action('pretix.event.order.expired', user=user, auth=auth)
i = order.invoices.filter(is_cancellation=False).last()
if i:
generate_cancellation(i)
@@ -263,7 +261,7 @@ def mark_order_expired(order, user=None, api_token=None):
@transaction.atomic
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None):
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, oauth_application=None):
"""
Mark this order as canceled
:param order: The order to change
@@ -275,13 +273,15 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None):
user = User.objects.get(pk=user)
if isinstance(api_token, int):
api_token = TeamAPIToken.objects.get(pk=api_token)
if isinstance(oauth_application, int):
oauth_application = OAuthApplication.objects.get(pk=oauth_application)
with order.event.lock():
if not order.cancel_allowed():
raise OrderError(_('You cannot cancel this order.'))
order.status = Order.STATUS_CANCELED
order.save()
order.log_action('pretix.event.order.canceled', user=user, api_token=api_token)
order.log_action('pretix.event.order.canceled', user=user, auth=api_token or oauth_application)
i = order.invoices.filter(is_cancellation=False).last()
if i:
generate_cancellation(i)
@@ -1163,10 +1163,10 @@ def perform_order(self, event: str, payment_provider: str, positions: List[str],
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None):
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None, oauth_application=None):
try:
try:
return _cancel_order(order, user, send_mail, api_token)
return _cancel_order(order, user, send_mail, api_token, oauth_application)
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):