From a62a2178d02d9a7c6c8582e7b6cf34591107d784 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 15 Oct 2016 00:49:59 +0800 Subject: [PATCH] Add user backend --- apps/common/utils.py | 26 +++++++++++++++++++---- apps/users/backends.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 apps/users/backends.py diff --git a/apps/common/utils.py b/apps/common/utils.py index ecbb293d6..a5af4f66c 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -7,15 +7,19 @@ from itertools import chain import string import logging -from itsdangerous import TimedJSONWebSignatureSerializer +from itsdangerous import Signer, TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, TimestampSigner, \ + BadSignature, SignatureExpired from django.shortcuts import reverse as dj_reverse from django.conf import settings from django.core import signing from django.utils import timezone +SECRET_KEY = settings.SECRET_KEY +SIGNER = TimestampSigner(SECRET_KEY) -def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None, external=False): - url = dj_reverse(viewname, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app) + +def reverse(view_name, urlconf=None, args=None, kwargs=None, current_app=None, external=False): + url = dj_reverse(view_name, urlconf=urlconf, args=args, kwargs=kwargs, current_app=current_app) if external: url = settings.SITE_URL.strip('/') + url @@ -44,13 +48,27 @@ def decrypt(*args, **kwargs): return '' +def sign(value): + return SIGNER.sign(value) + + +def unsign(value, max_age=3600): + try: + return SIGNER.unsign(value, max_age=max_age) + except (BadSignature, SignatureExpired): + return None + + def date_expired_default(): try: years = int(settings.CONFIG.DEFAULT_EXPIRED_YEARS) except TypeError: years = 70 + return timezone.now() + timezone.timedelta(days=365*years) + - return timezone.now() + timezone.timedelta(days=365 * years) +def sign(value): + return SIGNER.sign(value) def combine_seq(s1, s2, callback=None): diff --git a/apps/users/backends.py b/apps/users/backends.py new file mode 100644 index 000000000..ac4d67ddd --- /dev/null +++ b/apps/users/backends.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# + +from rest_framework import authentication, exceptions +from django.utils.translation import ugettext as _ + +from common.utils import unsign +from .models import User + + +class APPSignAuthentication(authentication.BaseAuthentication): + keyword = 'Sign' + model = User + + def authenticate(self, request): + auth = authentication.get_authorization_header(request).split() + + if not auth or auth[0].lower() != self.keyword.lower().encode(): + return None + + if len(auth) == 1: + msg = _('Invalid sign header. No credentials provided.') + raise exceptions.AuthenticationFailed(msg) + elif len(auth) > 2: + msg = _('Invalid sign header. Sign string should not contain spaces.') + raise exceptions.AuthenticationFailed(msg) + + try: + sign = auth[1].decode() + except UnicodeError: + msg = _('Invalid token header. Sign string should not contain invalid characters.') + raise exceptions.AuthenticationFailed(msg) + + return self.authenticate_credentials(sign) + + def authenticate_credentials(self, key): + try: + token = self.model.objects.select_related('user').get(key=key) + except self.model.DoesNotExist: + raise exceptions.AuthenticationFailed(_('Invalid token.')) + + if not token.user.is_active: + raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + + +if __name__ == '__main__': + pass