123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- import json
- from django.core.signing import TimestampSigner, BadSignature
- from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
- from django.utils.encoding import DjangoUnicodeDecodeError
- from .models import Contrib
- class URLTokenManager(TimestampSigner):
- """ Handle signed json data as URL-safe strings
- This class has two responsibilities:
- - sign/unsign
- - pack/unpack JSON to base64
- """
- def sign(self, payload, *args, **kwargs):
- """
- :param payload: the data to be embeded into the token
- :type data: dict
- """
- return urlsafe_base64_encode(
- super().sign(
- json.dumps(payload), *args, **kwargs
- ).encode('utf-8')).decode('ascii')
- def unsign(self, encoded_payload, *args, **kwargs):
- decoded_payload = urlsafe_base64_decode(encoded_payload)
- unsigned = super().unsign(decoded_payload)
- return json.loads(unsigned)
- class TokenError(Exception):
- pass
- class ContribTokenManager:
- """Produce and use signed URL tokens for account-less contrib management
- """
- SCOPE = 'contrib/manage'
- def __init__(self):
- self.manager = URLTokenManager()
- def mk_token(self, contrib):
- """ Generate a signed contrib management token
- Valid for a given contrib, and for a limited time.
- :type contrib: Contrib
- :rtype str:
- """
- return self.manager.sign({'scope': self.SCOPE, 'pk': contrib.pk})
- def get_instance_if_allowed(self, encoded_token, pk=None):
- """Return a contrib if the token grants authz for that Contrib pk
- Raise a TokenError if not authorized.
- :param pk: the contrib pk (optional, if you want to check that the
- instance is the right one)
- :param encoded_token: the encoded token, from ``mk_token()``:
- :return: a Contrib instance or None, if the object does not exist
- :rtype Contrib:
- """
- try:
- data = self.manager.unsign(encoded_token)
- except BadSignature:
- raise TokenError('Invalid token signature')
- except (DjangoUnicodeDecodeError, UnicodeDecodeError):
- raise TokenError('This is not a well-formed token')
- if (pk is not None) and (data['pk'] != pk):
- raise TokenError('Token is not valid for id {}'.format(pk))
- else:
- try:
- return Contrib.objects.get(pk=data['pk'])
- except Contrib.DoesNotExist:
- return None
|