diff --git a/dj_rest_auth/__version__.py b/dj_rest_auth/__version__.py index 475adb3b..f1918536 100644 --- a/dj_rest_auth/__version__.py +++ b/dj_rest_auth/__version__.py @@ -1,7 +1,7 @@ __title__ = 'dj-rest-auth' __description__ = 'Authentication and Registration in Django Rest Framework.' __url__ = 'http://github.com/iMerica/dj-rest-auth' -__version__ = '2.1.4' +__version__ = '2.1.5' __author__ = '@iMerica https://github.com/iMerica' __author_email__ = 'imichael@pm.me' __license__ = 'MIT' diff --git a/dj_rest_auth/app_settings.py b/dj_rest_auth/app_settings.py index 543b2bea..3b6e7252 100644 --- a/dj_rest_auth/app_settings.py +++ b/dj_rest_auth/app_settings.py @@ -1,19 +1,27 @@ +from django.conf import settings + from dj_rest_auth.serializers import JWTSerializer as DefaultJWTSerializer -from dj_rest_auth.serializers import JWTSerializerWithExpiration as DefaultJWTSerializerWithExpiration +from dj_rest_auth.serializers import ( + JWTSerializerWithExpiration as DefaultJWTSerializerWithExpiration, +) from dj_rest_auth.serializers import LoginSerializer as DefaultLoginSerializer -from dj_rest_auth.serializers import \ - PasswordChangeSerializer as DefaultPasswordChangeSerializer -from dj_rest_auth.serializers import \ - PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer -from dj_rest_auth.serializers import \ - PasswordResetSerializer as DefaultPasswordResetSerializer +from dj_rest_auth.serializers import ( + PasswordChangeSerializer as DefaultPasswordChangeSerializer, +) +from dj_rest_auth.serializers import ( + PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer, +) +from dj_rest_auth.serializers import ( + PasswordResetSerializer as DefaultPasswordResetSerializer, +) from dj_rest_auth.serializers import TokenSerializer as DefaultTokenSerializer -from dj_rest_auth.serializers import \ - UserDetailsSerializer as DefaultUserDetailsSerializer -from django.conf import settings +from dj_rest_auth.serializers import ( + UserDetailsSerializer as DefaultUserDetailsSerializer, +) from .utils import default_create_token, import_callable + create_token = import_callable(getattr(settings, 'REST_AUTH_TOKEN_CREATOR', default_create_token)) serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {}) @@ -28,16 +36,20 @@ LoginSerializer = import_callable(serializers.get('LOGIN_SERIALIZER', DefaultLoginSerializer)) -PasswordResetSerializer = import_callable(serializers.get( - 'PASSWORD_RESET_SERIALIZER', DefaultPasswordResetSerializer -)) +PasswordResetSerializer = import_callable( + serializers.get( + 'PASSWORD_RESET_SERIALIZER', DefaultPasswordResetSerializer, + ), +) -PasswordResetConfirmSerializer = import_callable(serializers.get( - 'PASSWORD_RESET_CONFIRM_SERIALIZER', DefaultPasswordResetConfirmSerializer -)) +PasswordResetConfirmSerializer = import_callable( + serializers.get( + 'PASSWORD_RESET_CONFIRM_SERIALIZER', DefaultPasswordResetConfirmSerializer, + ), +) PasswordChangeSerializer = import_callable( - serializers.get('PASSWORD_CHANGE_SERIALIZER', DefaultPasswordChangeSerializer) + serializers.get('PASSWORD_CHANGE_SERIALIZER', DefaultPasswordChangeSerializer), ) JWT_AUTH_COOKIE = getattr(settings, 'JWT_AUTH_COOKIE', None) diff --git a/dj_rest_auth/jwt_auth.py b/dj_rest_auth/jwt_auth.py index df9af113..9896ffa5 100644 --- a/dj_rest_auth/jwt_auth.py +++ b/dj_rest_auth/jwt_auth.py @@ -21,7 +21,7 @@ def set_jwt_access_cookie(response, access_token): expires=access_token_expiration, secure=cookie_secure, httponly=cookie_httponly, - samesite=cookie_samesite + samesite=cookie_samesite, ) @@ -42,7 +42,7 @@ def set_jwt_refresh_cookie(response, refresh_token): secure=cookie_secure, httponly=cookie_httponly, samesite=cookie_samesite, - path=refresh_cookie_path + path=refresh_cookie_path, ) @@ -63,7 +63,7 @@ def unset_jwt_cookies(response): class CookieTokenRefreshSerializer(TokenRefreshSerializer): - refresh = serializers.CharField(required=False, help_text="WIll override cookie.") + refresh = serializers.CharField(required=False, help_text='WIll override cookie.') def extract_refresh_token(self): request = self.context['request'] @@ -85,7 +85,7 @@ def get_refresh_view(): """ Returns a Token Refresh CBV without a circular import """ from rest_framework_simplejwt.settings import api_settings as jwt_settings from rest_framework_simplejwt.views import TokenRefreshView - + class RefreshViewWithCookieSupport(TokenRefreshView): serializer_class = CookieTokenRefreshSerializer @@ -115,7 +115,7 @@ def enforce_csrf(self, request): reason = check.process_view(request, None, (), {}) if reason: # CSRF failed, bail with explicit error message - raise exceptions.PermissionDenied('CSRF Failed: %s' % reason) + raise exceptions.PermissionDenied(f'CSRF Failed: {reason}') def authenticate(self, request): cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) @@ -123,7 +123,7 @@ def authenticate(self, request): if header is None: if cookie_name: raw_token = request.COOKIES.get(cookie_name) - if getattr(settings, 'JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED', False): #True at your own risk + if getattr(settings, 'JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED', False): #True at your own risk self.enforce_csrf(request) elif raw_token is not None and getattr(settings, 'JWT_AUTH_COOKIE_USE_CSRF', False): self.enforce_csrf(request) diff --git a/dj_rest_auth/models.py b/dj_rest_auth/models.py index 84108212..8175fc08 100644 --- a/dj_rest_auth/models.py +++ b/dj_rest_auth/models.py @@ -1,4 +1,5 @@ from django.conf import settings from django.utils.module_loading import import_string + TokenModel = import_string(getattr(settings, 'REST_AUTH_TOKEN_MODEL', 'rest_framework.authtoken.models.Token')) diff --git a/dj_rest_auth/registration/app_settings.py b/dj_rest_auth/registration/app_settings.py index f0b6c629..3ff4f780 100644 --- a/dj_rest_auth/registration/app_settings.py +++ b/dj_rest_auth/registration/app_settings.py @@ -1,17 +1,20 @@ -from dj_rest_auth.registration.serializers import \ - RegisterSerializer as DefaultRegisterSerializer from django.conf import settings from rest_framework.permissions import AllowAny +from dj_rest_auth.registration.serializers import ( + RegisterSerializer as DefaultRegisterSerializer, +) + from ..utils import import_callable + serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {}) RegisterSerializer = import_callable(serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer)) def register_permission_classes(): - permission_classes = [AllowAny, ] - for klass in getattr(settings, 'REST_AUTH_REGISTER_PERMISSION_CLASSES', tuple()): + permission_classes = [AllowAny] + for klass in getattr(settings, 'REST_AUTH_REGISTER_PERMISSION_CLASSES', ()): permission_classes.append(import_callable(klass)) return tuple(permission_classes) diff --git a/dj_rest_auth/registration/serializers.py b/dj_rest_auth/registration/serializers.py index 414e8225..b1ccacc0 100644 --- a/dj_rest_auth/registration/serializers.py +++ b/dj_rest_auth/registration/serializers.py @@ -6,6 +6,7 @@ from rest_framework import serializers from rest_framework.reverse import reverse + try: from allauth.account import app_settings as allauth_settings from allauth.account.adapter import get_adapter @@ -15,7 +16,7 @@ from allauth.socialaccount.providers.base import AuthProcess from allauth.utils import email_address_exists, get_username_max_length except ImportError: - raise ImportError("allauth needs to be added to INSTALLED_APPS.") + raise ImportError('allauth needs to be added to INSTALLED_APPS.') class SocialAccountSerializer(serializers.ModelSerializer): @@ -68,11 +69,11 @@ def set_callback_url(self, view, adapter_class): try: self.callback_url = reverse( viewname=adapter_class.provider_id + '_callback', - request=self._get_request() + request=self._get_request(), ) except NoReverseMatch: raise serializers.ValidationError( - _("Define callback_url in view") + _('Define callback_url in view'), ) def validate(self, attrs): @@ -81,12 +82,12 @@ def validate(self, attrs): if not view: raise serializers.ValidationError( - _("View is not defined, pass it as a context variable") + _('View is not defined, pass it as a context variable'), ) adapter_class = getattr(view, 'adapter_class', None) if not adapter_class: - raise serializers.ValidationError(_("Define adapter_class in view")) + raise serializers.ValidationError(_('Define adapter_class in view')) adapter = adapter_class(request) app = adapter.get_provider().get_app(request) @@ -112,7 +113,7 @@ def validate(self, attrs): if not self.client_class: raise serializers.ValidationError( - _("Define client_class in view") + _('Define client_class in view'), ) provider = adapter.get_provider() @@ -127,19 +128,20 @@ def validate(self, attrs): scope, scope_delimiter=adapter.scope_delimiter, headers=adapter.headers, - basic_auth=adapter.basic_auth + basic_auth=adapter.basic_auth, ) token = client.get_access_token(code) access_token = token['access_token'] tokens_to_parse = {'access_token': access_token} # If available we add additional data to the dictionary - for key in ["refresh_token", "id_token", adapter.expires_in_key]: + for key in ['refresh_token', 'id_token', adapter.expires_in_key]: if key in token: tokens_to_parse[key] = token[key] else: raise serializers.ValidationError( - _("Incorrect input. access_token or code is required.")) + _('Incorrect input. access_token or code is required.'), + ) social_token = adapter.parse_token(tokens_to_parse) social_token.app = app @@ -148,7 +150,7 @@ def validate(self, attrs): login = self.get_social_login(adapter, app, social_token, token) complete_social_login(request, login) except HTTPError: - raise serializers.ValidationError(_("Incorrect value")) + raise serializers.ValidationError(_('Incorrect value')) if not login.is_existing: # We have an account already signed up in a different flow @@ -162,7 +164,7 @@ def validate(self, attrs): ).exists() if account_exists: raise serializers.ValidationError( - _("User is already registered with this e-mail address.") + _('User is already registered with this e-mail address.'), ) login.lookup() @@ -173,14 +175,14 @@ def validate(self, attrs): return attrs -class SocialConnectMixin(object): +class SocialConnectMixin: def get_social_login(self, *args, **kwargs): """ Set the social login process state to connect rather than login Refer to the implementation of get_social_login in base class and to the allauth.socialaccount.helpers module complete_social_login function. """ - social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs) + social_login = super().get_social_login(*args, **kwargs) social_login.state['process'] = AuthProcess.CONNECT return social_login @@ -193,7 +195,7 @@ class RegisterSerializer(serializers.Serializer): username = serializers.CharField( max_length=get_username_max_length(), min_length=allauth_settings.USERNAME_MIN_LENGTH, - required=allauth_settings.USERNAME_REQUIRED + required=allauth_settings.USERNAME_REQUIRED, ) email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED) password1 = serializers.CharField(write_only=True) @@ -208,7 +210,8 @@ def validate_email(self, email): if allauth_settings.UNIQUE_EMAIL: if email and email_address_exists(email): raise serializers.ValidationError( - _("A user is already registered with this e-mail address.")) + _('A user is already registered with this e-mail address.'), + ) return email def validate_password1(self, password): @@ -226,7 +229,7 @@ def get_cleaned_data(self): return { 'username': self.validated_data.get('username', ''), 'password1': self.validated_data.get('password1', ''), - 'email': self.validated_data.get('email', '') + 'email': self.validated_data.get('email', ''), } def save(self, request): diff --git a/dj_rest_auth/registration/urls.py b/dj_rest_auth/registration/urls.py index 04fc7f4a..e25be49e 100644 --- a/dj_rest_auth/registration/urls.py +++ b/dj_rest_auth/registration/urls.py @@ -3,6 +3,7 @@ from .views import RegisterView, VerifyEmailView + urlpatterns = [ path('', RegisterView.as_view(), name='rest_register'), path('verify-email/', VerifyEmailView.as_view(), name='rest_verify_email'), @@ -18,6 +19,8 @@ # If you don't want to use API on that step, then just use ConfirmEmailView # view from: # django-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py - re_path(r'^account-confirm-email/(?P[-:\w]+)/$', TemplateView.as_view(), - name='account_confirm_email'), + re_path( + r'^account-confirm-email/(?P[-:\w]+)/$', TemplateView.as_view(), + name='account_confirm_email', + ), ] diff --git a/dj_rest_auth/registration/views.py b/dj_rest_auth/registration/views.py index ef3e22ed..99aa5074 100644 --- a/dj_rest_auth/registration/views.py +++ b/dj_rest_auth/registration/views.py @@ -5,30 +5,33 @@ from allauth.socialaccount import signals from allauth.socialaccount.adapter import get_adapter as get_social_adapter from allauth.socialaccount.models import SocialAccount -from dj_rest_auth.app_settings import (JWTSerializer, TokenSerializer, - create_token) -from dj_rest_auth.models import TokenModel -from dj_rest_auth.registration.serializers import (SocialAccountSerializer, - SocialConnectSerializer, - SocialLoginSerializer, - VerifyEmailSerializer) -from dj_rest_auth.utils import jwt_encode -from dj_rest_auth.views import LoginView from django.conf import settings from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.decorators.debug import sensitive_post_parameters from rest_framework import status -from rest_framework.exceptions import NotFound, MethodNotAllowed +from rest_framework.exceptions import MethodNotAllowed, NotFound from rest_framework.generics import CreateAPIView, GenericAPIView, ListAPIView from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView +from dj_rest_auth.app_settings import ( + JWTSerializer, TokenSerializer, create_token, +) +from dj_rest_auth.models import TokenModel +from dj_rest_auth.registration.serializers import ( + SocialAccountSerializer, SocialConnectSerializer, SocialLoginSerializer, + VerifyEmailSerializer, +) +from dj_rest_auth.utils import jwt_encode +from dj_rest_auth.views import LoginView + from .app_settings import RegisterSerializer, register_permission_classes + sensitive_post_parameters_m = method_decorator( - sensitive_post_parameters('password1', 'password2') + sensitive_post_parameters('password1', 'password2'), ) @@ -40,18 +43,18 @@ class RegisterView(CreateAPIView): @sensitive_post_parameters_m def dispatch(self, *args, **kwargs): - return super(RegisterView, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) def get_response_data(self, user): if allauth_settings.EMAIL_VERIFICATION == \ allauth_settings.EmailVerificationMethod.MANDATORY: - return {"detail": _("Verification e-mail sent.")} + return {'detail': _('Verification e-mail sent.')} if getattr(settings, 'REST_USE_JWT', False): data = { 'user': user, 'access_token': self.access_token, - 'refresh_token': self.refresh_token + 'refresh_token': self.refresh_token, } return JWTSerializer(data, context=self.get_serializer_context()).data else: @@ -63,9 +66,11 @@ def create(self, request, *args, **kwargs): user = self.perform_create(serializer) headers = self.get_success_headers(serializer.data) - return Response(self.get_response_data(user), - status=status.HTTP_201_CREATED, - headers=headers) + return Response( + self.get_response_data(user), + status=status.HTTP_201_CREATED, + headers=headers, + ) def perform_create(self, serializer): user = serializer.save(self.request) @@ -76,9 +81,11 @@ def perform_create(self, serializer): else: create_token(self.token_model, user, serializer) - complete_signup(self.request._request, user, - allauth_settings.EMAIL_VERIFICATION, - None) + complete_signup( + self.request._request, user, + allauth_settings.EMAIL_VERIFICATION, + None, + ) return user @@ -183,7 +190,7 @@ def post(self, request, *args, **kwargs): signals.social_account_removed.send( sender=SocialAccount, request=self.request, - socialaccount=account + socialaccount=account, ) return Response(self.get_serializer(account).data) diff --git a/dj_rest_auth/serializers.py b/dj_rest_auth/serializers.py index 2f99fbb9..4dba894a 100644 --- a/dj_rest_auth/serializers.py +++ b/dj_rest_auth/serializers.py @@ -6,6 +6,7 @@ from django.utils.http import urlsafe_base64_decode as uid_decoder from django.utils.module_loading import import_string + try: from django.utils.translation import gettext_lazy as _ except ImportError: @@ -16,6 +17,7 @@ from .models import TokenModel + # Get the UserModel UserModel = get_user_model() @@ -40,8 +42,6 @@ def _validate_email(self, email, password): return user def _validate_username(self, username, password): - user = None - if username and password: user = self.authenticate(username=username, password=password) else: @@ -51,8 +51,6 @@ def _validate_username(self, username, password): return user def _validate_username_email(self, username, email, password): - user = None - if email and password: user = self.authenticate(email=email, password=password) elif username and password: @@ -101,12 +99,14 @@ def get_auth_user(self, username, email, password): return self.get_auth_user_using_allauth(username, email, password) return self.get_auth_user_using_orm(username, email, password) - def validate_auth_user_status(self, user): + @staticmethod + def validate_auth_user_status(user): if not user.is_active: msg = _('User account is disabled.') raise exceptions.ValidationError(msg) - def validate_email_verification_status(self, user): + @staticmethod + def validate_email_verification_status(user): from allauth.account import app_settings if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY: email_address = user.emailaddress_set.get(email=user.email) @@ -149,7 +149,8 @@ class UserDetailsSerializer(serializers.ModelSerializer): User model w/o password """ - def validate_username(self, username): + @staticmethod + def validate_username(username): if 'allauth.account' not in settings.INSTALLED_APPS: # We don't need to call the all-auth # username validator unless its installed @@ -165,13 +166,13 @@ class Meta: # UserModel.XYZ causing attribute error while importing other # classes from `serializers.py`. So, we need to check whether the auth model has # the attribute or not - if hasattr(UserModel, "USERNAME_FIELD"): + if hasattr(UserModel, 'USERNAME_FIELD'): extra_fields.append(UserModel.USERNAME_FIELD) - if hasattr(UserModel, "EMAIL_FIELD"): + if hasattr(UserModel, 'EMAIL_FIELD'): extra_fields.append(UserModel.EMAIL_FIELD) - if hasattr(UserModel, "first_name"): + if hasattr(UserModel, 'first_name'): extra_fields.append('first_name') - if hasattr(UserModel, "last_name"): + if hasattr(UserModel, 'last_name'): extra_fields.append('last_name') model = UserModel fields = ('pk', *extra_fields) @@ -196,8 +197,8 @@ def get_user(self, obj): JWTUserDetailsSerializer = import_string( rest_auth_serializers.get( 'USER_DETAILS_SERIALIZER', - 'dj_rest_auth.serializers.UserDetailsSerializer' - ) + 'dj_rest_auth.serializers.UserDetailsSerializer', + ), ) user_data = JWTUserDetailsSerializer(obj['user'], context=self.context).data @@ -220,6 +221,8 @@ class PasswordResetSerializer(serializers.Serializer): password_reset_form_class = PasswordResetForm + reset_form = None + def get_email_options(self): """Override this method to change default e-mail options""" return {} @@ -256,11 +259,14 @@ class PasswordResetConfirmSerializer(serializers.Serializer): set_password_form_class = SetPasswordForm + _errors = {} + user = None + set_password_form = None + def custom_validation(self, attrs): pass def validate(self, attrs): - self._errors = {} # Decode the uidb64 to uid to get User object try: @@ -275,7 +281,7 @@ def validate(self, attrs): self.custom_validation(attrs) # Construct SetPasswordForm instance self.set_password_form = self.set_password_form_class( - user=self.user, data=attrs + user=self.user, data=attrs, ) if not self.set_password_form.is_valid(): raise serializers.ValidationError(self.set_password_form.errors) @@ -293,14 +299,16 @@ class PasswordChangeSerializer(serializers.Serializer): set_password_form_class = SetPasswordForm + set_password_form = None + def __init__(self, *args, **kwargs): self.old_password_field_enabled = getattr( - settings, 'OLD_PASSWORD_FIELD_ENABLED', False + settings, 'OLD_PASSWORD_FIELD_ENABLED', False, ) self.logout_on_password_change = getattr( - settings, 'LOGOUT_ON_PASSWORD_CHANGE', False + settings, 'LOGOUT_ON_PASSWORD_CHANGE', False, ) - super(PasswordChangeSerializer, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if not self.old_password_field_enabled: self.fields.pop('old_password') @@ -312,17 +320,17 @@ def validate_old_password(self, value): invalid_password_conditions = ( self.old_password_field_enabled, self.user, - not self.user.check_password(value) + not self.user.check_password(value), ) if all(invalid_password_conditions): - err_msg = _("Your old password was entered incorrectly. Please enter it again.") + err_msg = _('Your old password was entered incorrectly. Please enter it again.') raise serializers.ValidationError(err_msg) return value def validate(self, attrs): self.set_password_form = self.set_password_form_class( - user=self.user, data=attrs + user=self.user, data=attrs, ) if not self.set_password_form.is_valid(): diff --git a/dj_rest_auth/social_serializers.py b/dj_rest_auth/social_serializers.py index 60b0c2c3..494816b3 100644 --- a/dj_rest_auth/social_serializers.py +++ b/dj_rest_auth/social_serializers.py @@ -2,6 +2,7 @@ from django.http import HttpRequest from rest_framework import serializers + # Import is needed only if we are using social login, in which # case the allauth.socialaccount will be declared if 'allauth.socialaccount' in settings.INSTALLED_APPS: @@ -33,8 +34,10 @@ def get_social_login(self, adapter, app, token, response): `allauth.socialaccount.SocialLoginView` instance """ request = self._get_request() - social_login = adapter.complete_login(request, app, token, - response=response) + social_login = adapter.complete_login( + request, app, token, + response=response, + ) social_login.token = token return social_login @@ -44,12 +47,12 @@ def validate(self, attrs): if not view: raise serializers.ValidationError( - "View is not defined, pass it as a context variable" + 'View is not defined, pass it as a context variable', ) adapter_class = getattr(view, 'adapter_class', None) if not adapter_class: - raise serializers.ValidationError("Define adapter_class in view") + raise serializers.ValidationError('Define adapter_class in view') adapter = adapter_class(request) app = adapter.get_provider().get_app(request) diff --git a/dj_rest_auth/tests/django_urls.py b/dj_rest_auth/tests/django_urls.py index 57f9d7d2..0675a2ad 100644 --- a/dj_rest_auth/tests/django_urls.py +++ b/dj_rest_auth/tests/django_urls.py @@ -4,15 +4,15 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.urls import urlpatterns + try: from django.contrib.auth.views import ( - logout, login, password_reset, - password_change, password_reset_confirm + login, logout, password_change, password_reset, password_reset_confirm, ) except ImportError: from django.contrib.auth.views import ( - LoginView, LogoutView, PasswordResetView, - PasswordChangeView, PasswordResetConfirmView + LoginView, LogoutView, PasswordChangeView, PasswordResetConfirmView, + PasswordResetView, ) logout = LogoutView.as_view() login = LoginView.as_view() @@ -29,14 +29,20 @@ url(r'^password_reset_from_email/$', password_reset, dict(from_email='staffmember@example.com')), url(r'^password_reset/custom_redirect/$', password_reset, dict(post_reset_redirect='/custom/')), url(r'^password_reset/custom_redirect/named/$', password_reset, dict(post_reset_redirect='password_reset')), - url(r'^password_reset/html_email_template/$', password_reset, - dict(html_email_template_name='registration/html_password_reset_email.html')), - url(r'^reset/custom/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', + url( + r'^password_reset/html_email_template/$', password_reset, + dict(html_email_template_name='registration/html_password_reset_email.html'), + ), + url( + r'^reset/custom/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', password_reset_confirm, - dict(post_reset_redirect='/custom/')), - url(r'^reset/custom/named/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', + dict(post_reset_redirect='/custom/'), + ), + url( + r'^reset/custom/named/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', password_reset_confirm, - dict(post_reset_redirect='password_reset')), + dict(post_reset_redirect='password_reset'), + ), url(r'^password_change/custom/$', password_change, dict(post_change_redirect='/custom/')), url(r'^password_change/custom/named/$', password_change, dict(post_change_redirect='password_reset')), url(r'^admin_password_reset/$', password_reset, dict(is_admin_site=True)), diff --git a/dj_rest_auth/tests/mixins.py b/dj_rest_auth/tests/mixins.py index 03c32e2d..1ecda64c 100644 --- a/dj_rest_auth/tests/mixins.py +++ b/dj_rest_auth/tests/mixins.py @@ -5,6 +5,7 @@ from django.utils.encoding import force_str from rest_framework import permissions, status + try: from django.urls import reverse except ImportError: @@ -27,7 +28,7 @@ def options(self, path, data='', content_type=MULTIPART_CONTENT, follow=False, * return self.generic('OPTIONS', path, data, content_type, **extra) -class TestsMixin(object): +class TestsMixin: """ base for API tests: * easy request calls, f.e.: self.post(url, data), self.get(url) @@ -47,9 +48,9 @@ def send_request(self, request_method, *args, **kwargs): # check_headers = kwargs.pop('check_headers', True) if hasattr(self, 'token'): if getattr(settings, 'REST_USE_JWT', False): - kwargs['HTTP_AUTHORIZATION'] = 'JWT %s' % self.token + kwargs['HTTP_AUTHORIZATION'] = f'JWT {self.token}' else: - kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token + kwargs['HTTP_AUTHORIZATION'] = f'Token {self.token}' self.response = request_func(*args, **kwargs) is_json = 'application/json' in self.response.get('content-type') @@ -93,8 +94,8 @@ def init(self): def _login(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK) diff --git a/dj_rest_auth/tests/settings.py b/dj_rest_auth/tests/settings.py index e038c45a..516f11b3 100644 --- a/dj_rest_auth/tests/settings.py +++ b/dj_rest_auth/tests/settings.py @@ -2,19 +2,20 @@ import os import sys + PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0]) logging.disable(logging.CRITICAL) ROOT_URLCONF = 'urls' STATIC_URL = '/static/' -STATIC_ROOT = '%s/staticserve' % PROJECT_ROOT +STATIC_ROOT = f'{PROJECT_ROOT}/staticserve' STATICFILES_DIRS = ( - ('global', '%s/static' % PROJECT_ROOT), + ('global', f'{PROJECT_ROOT}/static'), ) UPLOADS_DIR_NAME = 'uploads' -MEDIA_URL = '/%s/' % UPLOADS_DIR_NAME -MEDIA_ROOT = os.path.join(PROJECT_ROOT, '%s' % UPLOADS_DIR_NAME) +MEDIA_URL = f'/{UPLOADS_DIR_NAME}/' +MEDIA_ROOT = os.path.join(PROJECT_ROOT, f'{UPLOADS_DIR_NAME}') IS_DEV = False IS_STAGING = False @@ -25,7 +26,7 @@ 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', - } + }, } MIDDLEWARE = [ @@ -33,7 +34,7 @@ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware' + 'django.contrib.messages.middleware.MessageMiddleware', ] # Adding for backwards compatibility for Django 1.8 tests @@ -47,8 +48,8 @@ 'django.contrib.messages.context_processors.messages', 'django.core.context_processors.static', - "allauth.account.context_processors.account", - "allauth.socialaccount.context_processors.socialaccount", + 'allauth.account.context_processors.account', + 'allauth.socialaccount.context_processors.socialaccount', ] # avoid deprecation warnings during tests @@ -69,7 +70,7 @@ 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', - ) + ), } TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' @@ -98,10 +99,10 @@ 'dj_rest_auth', 'dj_rest_auth.registration', - 'rest_framework_simplejwt.token_blacklist' + 'rest_framework_simplejwt.token_blacklist', ] -SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd" +SECRET_KEY = '38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd' ACCOUNT_ACTIVATION_DAYS = 1 SITE_ID = 1 diff --git a/dj_rest_auth/tests/test_api.py b/dj_rest_auth/tests/test_api.py index 5310c651..50b02885 100644 --- a/dj_rest_auth/tests/test_api.py +++ b/dj_rest_auth/tests/test_api.py @@ -14,6 +14,7 @@ from .mixins import CustomPermissionClass, TestsMixin + try: from django.urls import reverse except ImportError: @@ -34,7 +35,7 @@ def get_token(cls, user): return token -@override_settings(ROOT_URLCONF="tests.urls") +@override_settings(ROOT_URLCONF='tests.urls') class APIBasicTests(TestsMixin, TestCase): """ Case #1: @@ -46,24 +47,24 @@ class APIBasicTests(TestsMixin, TestCase): USERNAME = 'person' PASS = 'person' - EMAIL = "person1@world.com" + EMAIL = 'person1@world.com' NEW_PASS = 'new-test-pass' REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView' # data without user profile REGISTRATION_DATA = { - "username": USERNAME, - "password1": PASS, - "password2": PASS + 'username': USERNAME, + 'password1': PASS, + 'password2': PASS, } REGISTRATION_DATA_WITH_EMAIL = REGISTRATION_DATA.copy() REGISTRATION_DATA_WITH_EMAIL['email'] = EMAIL BASIC_USER_DATA = { - 'first_name': "John", + 'first_name': 'John', 'last_name': 'Smith', - 'email': EMAIL + 'email': EMAIL, } USER_DATA = BASIC_USER_DATA.copy() USER_DATA['newsletter_subscribe'] = True @@ -84,36 +85,36 @@ def _generate_uid_and_token(self, user): @override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.EMAIL) def test_login_failed_email_validation(self): payload = { - "email": '', - "password": self.PASS + 'email': '', + 'password': self.PASS, } resp = self.post(self.login_url, data=payload, status_code=400) - self.assertEqual(resp.json['non_field_errors'][0], u'Must include "email" and "password".') + self.assertEqual(resp.json['non_field_errors'][0], 'Must include "email" and "password".') @override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.USERNAME) def test_login_failed_username_validation(self): payload = { - "username": '', - "password": self.PASS + 'username': '', + 'password': self.PASS, } resp = self.post(self.login_url, data=payload, status_code=400) - self.assertEqual(resp.json['non_field_errors'][0], u'Must include "username" and "password".') + self.assertEqual(resp.json['non_field_errors'][0], 'Must include "username" and "password".') @override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.USERNAME_EMAIL) def test_login_failed_username_email_validation(self): payload = { - "password": self.PASS + 'password': self.PASS, } resp = self.post(self.login_url, data=payload, status_code=400) - self.assertEqual(resp.json['non_field_errors'][0], u'Must include either "username" or "email" and "password".') + self.assertEqual(resp.json['non_field_errors'][0], 'Must include either "username" or "email" and "password".') def test_allauth_login_with_username(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } # there is no users in db so it should throw error (400) self.post(self.login_url, data=payload, status_code=400) @@ -136,8 +137,8 @@ def test_allauth_login_with_username(self): # test wrong username/password payload = { - "username": self.USERNAME + '?', - "password": self.PASS + 'username': self.USERNAME + '?', + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=400) @@ -147,8 +148,8 @@ def test_allauth_login_with_username(self): @override_settings(ACCOUNT_AUTHENTICATION_METHOD=account_app_settings.AuthenticationMethod.EMAIL) def test_allauth_login_with_email(self): payload = { - "email": self.EMAIL, - "password": self.PASS + 'email': self.EMAIL, + 'password': self.PASS, } # there is no users in db so it should throw error (400) self.post(self.login_url, data=payload, status_code=400) @@ -163,8 +164,8 @@ def test_allauth_login_with_email(self): @override_settings(REST_USE_JWT=True) def test_login_jwt(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) @@ -177,8 +178,8 @@ def test_login_by_email(self): settings.INSTALLED_APPS.remove('allauth') payload = { - "email": self.EMAIL.lower(), - "password": self.PASS + 'email': self.EMAIL.lower(), + 'password': self.PASS, } # there is no users in db so it should throw error (400) self.post(self.login_url, data=payload, status_code=400) @@ -195,8 +196,8 @@ def test_login_by_email(self): # test auth by email in different case payload = { - "email": self.EMAIL.upper(), - "password": self.PASS + 'email': self.EMAIL.upper(), + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=200) self.assertEqual('key' in self.response.json.keys(), True) @@ -209,8 +210,8 @@ def test_login_by_email(self): # test wrong email/password payload = { - "email": 't' + self.EMAIL, - "password": self.PASS + 'email': 't' + self.EMAIL, + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=400) @@ -222,21 +223,21 @@ def test_login_by_email(self): def test_password_change(self): login_payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=login_payload, status_code=200) self.token = self.response.json['key'] new_password_payload = { - "new_password1": "new_person", - "new_password2": "new_person" + 'new_password1': 'new_person', + 'new_password2': 'new_person', } self.post( self.password_change_url, data=new_password_payload, - status_code=200 + status_code=200, ) # user should not be able to login using old password @@ -248,13 +249,13 @@ def test_password_change(self): # pass1 and pass2 are not equal new_password_payload = { - "new_password1": "new_person1", - "new_password2": "new_person" + 'new_password1': 'new_person1', + 'new_password2': 'new_person', } self.post( self.password_change_url, data=new_password_payload, - status_code=400 + status_code=400, ) # send empty payload @@ -263,33 +264,33 @@ def test_password_change(self): @override_settings(OLD_PASSWORD_FIELD_ENABLED=True) def test_password_change_with_old_password(self): login_payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=login_payload, status_code=200) self.token = self.response.json['key'] new_password_payload = { - "old_password": "%s!" % self.PASS, # wrong password - "new_password1": "new_person", - "new_password2": "new_person" + 'old_password': f'{self.PASS}!', # wrong password + 'new_password1': 'new_person', + 'new_password2': 'new_person', } self.post( self.password_change_url, data=new_password_payload, - status_code=400 + status_code=400, ) new_password_payload = { - "old_password": self.PASS, - "new_password1": "new_person", - "new_password2": "new_person" + 'old_password': self.PASS, + 'new_password1': 'new_person', + 'new_password2': 'new_person', } self.post( self.password_change_url, data=new_password_payload, - status_code=200 + status_code=200, ) # user should not be able to login using old password @@ -316,7 +317,7 @@ def test_password_reset(self): 'new_password1': self.NEW_PASS, 'new_password2': self.NEW_PASS, 'uid': force_str(url_kwargs['uid']), - 'token': '-wrong-token-' + 'token': '-wrong-token-', } self.post(url, data=data, status_code=400) @@ -325,7 +326,7 @@ def test_password_reset(self): 'new_password1': self.NEW_PASS, 'new_password2': self.NEW_PASS, 'uid': '-wrong-uid-', - 'token': url_kwargs['token'] + 'token': url_kwargs['token'], } self.post(url, data=data, status_code=400) @@ -334,7 +335,7 @@ def test_password_reset(self): 'new_password1': self.NEW_PASS, 'new_password2': self.NEW_PASS, 'uid': '-wrong-uid-', - 'token': '-wrong-token-' + 'token': '-wrong-token-', } self.post(url, data=data, status_code=400) @@ -343,14 +344,14 @@ def test_password_reset(self): 'new_password1': self.NEW_PASS, 'new_password2': self.NEW_PASS, 'uid': force_str(url_kwargs['uid']), - 'token': url_kwargs['token'] + 'token': url_kwargs['token'], } url = reverse('rest_password_reset_confirm') self.post(url, data=data, status_code=200) payload = { - "username": self.USERNAME, - "password": self.NEW_PASS + 'username': self.USERNAME, + 'password': self.NEW_PASS, } self.post(self.login_url, data=payload, status_code=200) @@ -378,8 +379,8 @@ def test_password_reset_with_invalid_email(self): def test_user_details(self): user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=200) self.token = self.response.json['key'] @@ -395,8 +396,8 @@ def test_user_details(self): def test_user_details_using_jwt(self): user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } self.post(self.login_url, data=payload, status_code=200) self.token = self.response.json['access_token'] @@ -457,7 +458,7 @@ def test_registration_with_invalid_password(self): @override_settings( ACCOUNT_EMAIL_VERIFICATION='mandatory', ACCOUNT_EMAIL_REQUIRED=True, - ACCOUNT_EMAIL_CONFIRMATION_HMAC=False + ACCOUNT_EMAIL_CONFIRMATION_HMAC=False, ) def test_registration_with_email_verification(self): user_count = get_user_model().objects.all().count() @@ -467,13 +468,13 @@ def test_registration_with_email_verification(self): self.post( self.register_url, data={}, - status_code=status.HTTP_400_BAD_REQUEST + status_code=status.HTTP_400_BAD_REQUEST, ) result = self.post( self.register_url, data=self.REGISTRATION_DATA_WITH_EMAIL, - status_code=status.HTTP_201_CREATED + status_code=status.HTTP_201_CREATED, ) self.assertNotIn('key', result.data) self.assertEqual(get_user_model().objects.all().count(), user_count + 1) @@ -483,20 +484,20 @@ def test_registration_with_email_verification(self): # test browsable endpoint result = self.get( - self.verify_email_url + self.verify_email_url, ) self.assertEqual(result.status_code, 405) self.assertEqual(result.json['detail'], 'Method "GET" not allowed.') # email is not verified yet payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } self.post( self.login_url, data=payload, - status=status.HTTP_400_BAD_REQUEST + status=status.HTTP_400_BAD_REQUEST, ) # verify email @@ -504,8 +505,8 @@ def test_registration_with_email_verification(self): .emailconfirmation_set.order_by('-created')[0] self.post( self.verify_email_url, - data={"key": email_confirmation.key}, - status_code=status.HTTP_200_OK + data={'key': email_confirmation.key}, + status_code=status.HTTP_200_OK, ) # try to login again @@ -515,8 +516,8 @@ def test_registration_with_email_verification(self): @override_settings(ACCOUNT_LOGOUT_ON_GET=True) def test_logout_on_get(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } # create user @@ -528,8 +529,8 @@ def test_logout_on_get(self): @override_settings(ACCOUNT_LOGOUT_ON_GET=False) def test_logout_on_post_only(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } # create user @@ -542,8 +543,8 @@ def test_logout_on_post_only(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') def test_login_jwt_sets_cookie(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) resp = self.post(self.login_url, data=payload, status_code=200) @@ -553,8 +554,8 @@ def test_login_jwt_sets_cookie(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') def test_logout_jwt_deletes_cookie(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) @@ -566,8 +567,8 @@ def test_logout_jwt_deletes_cookie(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') def test_logout_jwt_deletes_cookie_refresh(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) @@ -577,16 +578,18 @@ def test_logout_jwt_deletes_cookie_refresh(self): @override_settings(REST_USE_JWT=True) @override_settings(JWT_AUTH_COOKIE='jwt-auth') - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) def test_cookie_authentication(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) resp = self.post(self.login_url, data=payload, status_code=200) @@ -598,23 +601,25 @@ def test_cookie_authentication(self): def test_blacklisting_not_installed(self): settings.INSTALLED_APPS.remove('rest_framework_simplejwt.token_blacklist') payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) resp = self.post(self.login_url, data=payload, status_code=200) token = resp.data['refresh_token'] resp = self.post(self.logout_url, status=200, data={'refresh': token}) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.data["detail"], - "Neither cookies or blacklist are enabled, so the token has not been deleted server side. " - "Please make sure the token is deleted client side.") + self.assertEqual( + resp.data['detail'], + 'Neither cookies or blacklist are enabled, so the token has not been deleted server side. ' + 'Please make sure the token is deleted client side.', + ) @override_settings(REST_USE_JWT=True) def test_blacklisting(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) resp = self.post(self.login_url, data=payload, status_code=200) @@ -632,21 +637,23 @@ def test_blacklisting(self): @override_settings(REST_USE_JWT=True) @override_settings(JWT_AUTH_COOKIE=None) - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings( REST_AUTH_SERIALIZERS={ - "JWT_TOKEN_CLAIMS_SERIALIZER": 'tests.test_api.TESTTokenObtainPairSerializer' - } + 'JWT_TOKEN_CLAIMS_SERIALIZER': 'tests.test_api.TESTTokenObtainPairSerializer', + }, ) def test_custom_jwt_claims(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) @@ -661,21 +668,23 @@ def test_custom_jwt_claims(self): @override_settings(REST_USE_JWT=True) @override_settings(JWT_AUTH_COOKIE='jwt-auth') - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings( REST_AUTH_SERIALIZERS={ - "JWT_TOKEN_CLAIMS_SERIALIZER": 'tests.test_api.TESTTokenObtainPairSerializer' - } + 'JWT_TOKEN_CLAIMS_SERIALIZER': 'tests.test_api.TESTTokenObtainPairSerializer', + }, ) def test_custom_jwt_claims_cookie_w_authentication(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) resp = self.post(self.login_url, data=payload, status_code=200) @@ -693,23 +702,25 @@ def test_custom_jwt_claims_cookie_w_authentication(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') @override_settings(JWT_AUTH_COOKIE_USE_CSRF=False) @override_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=False) - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings(CSRF_COOKIE_SECURE =True) @override_settings(CSRF_COOKIE_HTTPONLY =True) - def test_wo_csrf_enforcement(self): + def test_wo_csrf_enforcement(self): from .mixins import APIClient payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } client = APIClient(enforce_csrf_checks=True) get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - + resp = client.post(self.login_url, payload) self.assertTrue('jwt-auth' in list(client.cookies.keys())) self.assertEquals(resp.status_code, 200) @@ -734,26 +745,28 @@ def test_wo_csrf_enforcement(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') @override_settings(JWT_AUTH_COOKIE_USE_CSRF=True) @override_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=False) - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings(CSRF_COOKIE_SECURE =True) @override_settings(CSRF_COOKIE_HTTPONLY =True) - def test_csrf_wo_login_csrf_enforcement(self): + def test_csrf_wo_login_csrf_enforcement(self): from .mixins import APIClient payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } client = APIClient(enforce_csrf_checks=True) get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - - response = client.get(reverse("getcsrf")) + + client.get(reverse('getcsrf')) csrftoken = client.cookies['csrftoken'].value - + resp = client.post(self.login_url, payload) self.assertTrue('jwt-auth' in list(client.cookies.keys())) self.assertTrue('csrftoken' in list(client.cookies.keys())) @@ -778,7 +791,7 @@ def test_csrf_wo_login_csrf_enforcement(self): resp = client.post('/protected-view/', {}) self.assertEquals(resp.status_code, 403) - csrfparam = {"csrfmiddlewaretoken": csrftoken} + csrfparam = {'csrfmiddlewaretoken': csrftoken} resp = client.post('/protected-view/', csrfparam) self.assertEquals(resp.status_code, 200) @@ -787,26 +800,28 @@ def test_csrf_wo_login_csrf_enforcement(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') @override_settings(JWT_AUTH_COOKIE_USE_CSRF=True) @override_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) #True at your own risk - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings(CSRF_COOKIE_SECURE =True) @override_settings(CSRF_COOKIE_HTTPONLY =True) - def test_csrf_w_login_csrf_enforcement(self): + def test_csrf_w_login_csrf_enforcement(self): from .mixins import APIClient payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } client = APIClient(enforce_csrf_checks=True) get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - - response = client.get(reverse("getcsrf")) + + client.get(reverse('getcsrf')) csrftoken = client.cookies['csrftoken'].value - + #fail w/o csrftoken in payload resp = client.post(self.login_url, payload) self.assertEquals(resp.status_code, 403) @@ -817,7 +832,7 @@ def test_csrf_w_login_csrf_enforcement(self): self.assertTrue('csrftoken' in list(client.cookies.keys())) self.assertEquals(resp.status_code, 200) - ## TEST WITH JWT AUTH HEADER does not make sense + ## TEST WITH JWT AUTH HEADER does not make sense ## TEST WITH COOKIES resp = client.get('/protected-view/') @@ -826,7 +841,7 @@ def test_csrf_w_login_csrf_enforcement(self): resp = client.post('/protected-view/', {}) self.assertEquals(resp.status_code, 403) - csrfparam = {"csrfmiddlewaretoken": csrftoken} + csrfparam = {'csrfmiddlewaretoken': csrftoken} resp = client.post('/protected-view/', csrfparam) self.assertEquals(resp.status_code, 200) @@ -835,26 +850,28 @@ def test_csrf_w_login_csrf_enforcement(self): @override_settings(JWT_AUTH_COOKIE='jwt-auth') @override_settings(JWT_AUTH_COOKIE_USE_CSRF=False) @override_settings(JWT_AUTH_COOKIE_ENFORCE_CSRF_ON_UNAUTHENTICATED=True) #True at your own risk - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.jwt_auth.JWTCookieAuthentication' - ] - )) + @override_settings( + REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', + ], + ), + ) @override_settings(REST_SESSION_LOGIN=False) @override_settings(CSRF_COOKIE_SECURE =True) @override_settings(CSRF_COOKIE_HTTPONLY =True) - def test_csrf_w_login_csrf_enforcement_2(self): + def test_csrf_w_login_csrf_enforcement_2(self): from .mixins import APIClient payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } client = APIClient(enforce_csrf_checks=True) get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - - response = client.get(reverse("getcsrf")) + + client.get(reverse('getcsrf')) csrftoken = client.cookies['csrftoken'].value - + #fail w/o csrftoken in payload resp = client.post(self.login_url, payload) self.assertEquals(resp.status_code, 403) @@ -865,7 +882,7 @@ def test_csrf_w_login_csrf_enforcement_2(self): self.assertTrue('csrftoken' in list(client.cookies.keys())) self.assertEquals(resp.status_code, 200) - ## TEST WITH JWT AUTH HEADER does not make sense + ## TEST WITH JWT AUTH HEADER does not make sense ## TEST WITH COOKIES resp = client.get('/protected-view/') @@ -874,7 +891,7 @@ def test_csrf_w_login_csrf_enforcement_2(self): resp = client.post('/protected-view/', {}) self.assertEquals(resp.status_code, 403) - csrfparam = {"csrfmiddlewaretoken": csrftoken} + csrfparam = {'csrfmiddlewaretoken': csrftoken} resp = client.post('/protected-view/', csrfparam) self.assertEquals(resp.status_code, 200) @@ -883,8 +900,8 @@ def test_csrf_w_login_csrf_enforcement_2(self): @override_settings(ACCOUNT_LOGOUT_ON_GET=True) def test_return_expiration(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } # create user @@ -902,8 +919,8 @@ def test_return_expiration(self): @override_settings(JWT_AUTH_REFRESH_COOKIE_PATH='/foo/bar') def test_refresh_cookie_name(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } # create user @@ -920,8 +937,8 @@ def test_refresh_cookie_name(self): @override_settings(JWT_AUTH_REFRESH_COOKIE='refresh-xxx') def test_custom_token_refresh_view(self): payload = { - "username": self.USERNAME, - "password": self.PASS + 'username': self.USERNAME, + 'password': self.PASS, } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) @@ -930,6 +947,6 @@ def test_custom_token_refresh_view(self): refresh_resp = self.post( reverse('token_refresh'), data=dict(refresh=refresh), - status_code=200 + status_code=200, ) self.assertIn('xxx', refresh_resp.cookies) diff --git a/dj_rest_auth/tests/test_serializers.py b/dj_rest_auth/tests/test_serializers.py index f4238f47..1b21d8f9 100644 --- a/dj_rest_auth/tests/test_serializers.py +++ b/dj_rest_auth/tests/test_serializers.py @@ -5,54 +5,55 @@ from dj_rest_auth.serializers import UserDetailsSerializer + User = get_user_model() def validate_is_lower(username: str): if username.islower(): return username - raise ValidationError("username must be lower case") + raise ValidationError('username must be lower case') custom_username_validators = [validate_is_lower] -validator_path = "dj_rest_auth.tests.test_serializers.custom_username_validators" +validator_path = 'dj_rest_auth.tests.test_serializers.custom_username_validators' class TestUserDetailsSerializer(TestCase): @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( - username="alice", email="alice@test.com", first_name="Alice" + username='alice', email='alice@test.com', first_name='Alice', ) @override_settings(ACCOUNT_USERNAME_VALIDATORS=validator_path) def test_validate_username_with_all_auth_failure(self): serializer = UserDetailsSerializer( - self.user, data={"username": "TestUsername"}, partial=True + self.user, data={'username': 'TestUsername'}, partial=True, ) self.assertEqual(False, serializer.is_valid()) self.assertEqual( serializer.errors, { - "username": [ - ErrorDetail(string="username must be lower case", code="invalid") - ] + 'username': [ + ErrorDetail(string='username must be lower case', code='invalid'), + ], }, ) @override_settings(ACCOUNT_USERNAME_VALIDATORS=validator_path) def test_validate_username_with_all_auth_success(self): serializer = UserDetailsSerializer( - self.user, data={"username": "test_username"}, partial=True + self.user, data={'username': 'test_username'}, partial=True, ) self.assertEqual(True, serializer.is_valid()) - self.assertEqual(serializer.validated_data, {"username": "test_username"}) + self.assertEqual(serializer.validated_data, {'username': 'test_username'}) - @modify_settings(INSTALLED_APPS={"remove": ["allauth", "allauth.account"]}) + @modify_settings(INSTALLED_APPS={'remove': ['allauth', 'allauth.account']}) @override_settings(ACCOUNT_USERNAME_VALIDATORS=validator_path) def test_validate_username_without_all_auth(self): serializer = UserDetailsSerializer( - self.user, data={"username": "TestUsername"}, partial=True + self.user, data={'username': 'TestUsername'}, partial=True, ) self.assertEqual(True, serializer.is_valid()) - self.assertEqual(serializer.validated_data, {"username": "TestUsername"}) + self.assertEqual(serializer.validated_data, {'username': 'TestUsername'}) diff --git a/dj_rest_auth/tests/test_social.py b/dj_rest_auth/tests/test_social.py index 819b1534..d42cd148 100644 --- a/dj_rest_auth/tests/test_social.py +++ b/dj_rest_auth/tests/test_social.py @@ -11,23 +11,24 @@ from .mixins import TestsMixin + try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse -@override_settings(ROOT_URLCONF="tests.urls") +@override_settings(ROOT_URLCONF='tests.urls') class TestSocialAuth(TestsMixin, TestCase): USERNAME = 'person' PASS = 'person' - EMAIL = "person1@world.com" + EMAIL = 'person1@world.com' REGISTRATION_DATA = { - "username": USERNAME, - "password1": PASS, - "password2": PASS, - "email": EMAIL + 'username': USERNAME, + 'password1': PASS, + 'password2': PASS, + 'email': EMAIL, } def setUp(self): @@ -61,11 +62,11 @@ def test_failed_social_auth(self): self.graph_api_url, body='', status=400, - content_type='application/json' + content_type='application/json', ) payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_login_url, data=payload, status_code=400) @@ -73,17 +74,17 @@ def test_failed_social_auth(self): def test_social_auth(self): # fake response for facebook call resp_body = { - "id": "123123123123", - "first_name": "John", - "gender": "male", - "last_name": "Smith", - "link": "https://www.facebook.com/john.smith", - "locale": "en_US", - "name": "John Smith", - "timezone": 2, - "updated_time": "2014-08-13T10:14:38+0000", - "username": "john.smith", - "verified": True + 'id': '123123123123', + 'first_name': 'John', + 'gender': 'male', + 'last_name': 'Smith', + 'link': 'https://www.facebook.com/john.smith', + 'locale': 'en_US', + 'name': 'John Smith', + 'timezone': 2, + 'updated_time': '2014-08-13T10:14:38+0000', + 'username': 'john.smith', + 'verified': True, } responses.add( @@ -91,12 +92,12 @@ def test_social_auth(self): self.graph_api_url, body=json.dumps(resp_body), status=200, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_login_url, data=payload, status_code=200) @@ -111,7 +112,7 @@ def test_social_auth(self): def _twitter_social_auth(self): # fake response for twitter call resp_body = { - "id": "123123123123", + 'id': '123123123123', } responses.add( @@ -119,13 +120,13 @@ def _twitter_social_auth(self): 'https://api.twitter.com/1.1/account/verify_credentials.json', body=json.dumps(resp_body), status=200, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { 'access_token': 'abc123', - 'token_secret': '1111222233334444' + 'token_secret': '1111222233334444', } self.post(self.tw_login_url, data=payload) @@ -152,7 +153,7 @@ def test_twitter_social_auth_without_auto_singup(self): def test_twitter_social_auth_request_error(self): # fake response for twitter call resp_body = { - "id": "123123123123", + 'id': '123123123123', } responses.add( @@ -160,13 +161,13 @@ def test_twitter_social_auth_request_error(self): 'https://api.twitter.com/1.1/account/verify_credentials.json', body=json.dumps(resp_body), status=400, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { 'access_token': 'abc123', - 'token_secret': '1111222233334444' + 'token_secret': '1111222233334444', } self.post(self.tw_login_url, data=payload, status_code=400) @@ -177,7 +178,7 @@ def test_twitter_social_auth_request_error(self): def test_twitter_social_auth_no_view_in_context(self): # fake response for twitter call resp_body = { - "id": "123123123123", + 'id': '123123123123', } responses.add( @@ -185,13 +186,13 @@ def test_twitter_social_auth_no_view_in_context(self): 'https://api.twitter.com/1.1/account/verify_credentials.json', body=json.dumps(resp_body), status=400, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { 'access_token': 'abc123', - 'token_secret': '1111222233334444' + 'token_secret': '1111222233334444', } self.post(self.tw_login_no_view_url, data=payload, status_code=400) @@ -201,7 +202,7 @@ def test_twitter_social_auth_no_view_in_context(self): def test_twitter_social_auth_no_adapter(self): # fake response for twitter call resp_body = { - "id": "123123123123", + 'id': '123123123123', } responses.add( @@ -209,13 +210,13 @@ def test_twitter_social_auth_no_adapter(self): 'https://api.twitter.com/1.1/account/verify_credentials.json', body=json.dumps(resp_body), status=400, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { 'access_token': 'abc123', - 'token_secret': '1111222233334444' + 'token_secret': '1111222233334444', } self.post(self.tw_login_no_adapter_url, data=payload, status_code=400) @@ -226,22 +227,22 @@ def test_twitter_social_auth_no_adapter(self): ACCOUNT_EMAIL_VERIFICATION='mandatory', ACCOUNT_EMAIL_REQUIRED=True, REST_SESSION_LOGIN=False, - ACCOUNT_EMAIL_CONFIRMATION_HMAC=False + ACCOUNT_EMAIL_CONFIRMATION_HMAC=False, ) def test_email_clash_with_existing_account(self): resp_body = { - "id": "123123123123", - "first_name": "John", - "gender": "male", - "last_name": "Smith", - "link": "https://www.facebook.com/john.smith", - "locale": "en_US", - "name": "John Smith", - "timezone": 2, - "updated_time": "2014-08-13T10:14:38+0000", - "username": "john.smith", - "verified": True, - "email": self.EMAIL + 'id': '123123123123', + 'first_name': 'John', + 'gender': 'male', + 'last_name': 'Smith', + 'link': 'https://www.facebook.com/john.smith', + 'locale': 'en_US', + 'name': 'John Smith', + 'timezone': 2, + 'updated_time': '2014-08-13T10:14:38+0000', + 'username': 'john.smith', + 'verified': True, + 'email': self.EMAIL, } responses.add( @@ -249,7 +250,7 @@ def test_email_clash_with_existing_account(self): self.graph_api_url, body=json.dumps(resp_body), status=200, - content_type='application/json' + content_type='application/json', ) # test empty payload @@ -259,7 +260,7 @@ def test_email_clash_with_existing_account(self): self.post( self.register_url, data=self.REGISTRATION_DATA, - status_code=201 + status_code=201, ) new_user = get_user_model().objects.latest('id') self.assertEqual(new_user.username, self.REGISTRATION_DATA['username']) @@ -269,8 +270,8 @@ def test_email_clash_with_existing_account(self): .emailconfirmation_set.order_by('-created')[0] self.post( self.verify_email_url, - data={"key": email_confirmation.key}, - status_code=status.HTTP_200_OK + data={'key': email_confirmation.key}, + status_code=status.HTTP_200_OK, ) self._login() @@ -278,13 +279,13 @@ def test_email_clash_with_existing_account(self): # fb log in with already existing email payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_login_url, data=payload, status_code=400) @responses.activate @override_settings( - REST_USE_JWT=True + REST_USE_JWT=True, ) def test_jwt(self): resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}' # noqa @@ -293,12 +294,12 @@ def test_jwt(self): self.graph_api_url, body=resp_body, status=200, - content_type='application/json' + content_type='application/json', ) users_count = get_user_model().objects.all().count() payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_login_url, data=payload, status_code=200) @@ -308,17 +309,17 @@ def test_jwt(self): self.assertEqual(get_user_model().objects.all().count(), users_count + 1) -@override_settings(ROOT_URLCONF="tests.urls") +@override_settings(ROOT_URLCONF='tests.urls') class TestSocialConnectAuth(TestsMixin, TestCase): USERNAME = 'person' PASS = 'person' - EMAIL = "person1@world.com" + EMAIL = 'person1@world.com' REGISTRATION_DATA = { - "username": USERNAME, - "password1": PASS, - "password2": PASS, - "email": EMAIL + 'username': USERNAME, + 'password1': PASS, + 'password2': PASS, + 'email': EMAIL, } def setUp(self): @@ -351,11 +352,11 @@ def test_social_connect_no_auth(self): self.graph_api_url, body='', status=200, - content_type='application/json' + content_type='application/json', ) payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_connect_url, data=payload, status_code=403) self.post(self.tw_connect_url, data=payload, status_code=403) @@ -366,22 +367,22 @@ def test_social_connect(self): self.post( self.register_url, data=self.REGISTRATION_DATA, - status_code=201 + status_code=201, ) # Test Facebook resp_body = { - "id": "123123123123", - "first_name": "John", - "gender": "male", - "last_name": "Smith", - "link": "https://www.facebook.com/john.smith", - "locale": "en_US", - "name": "John Smith", - "timezone": 2, - "updated_time": "2014-08-13T10:14:38+0000", - "username": "john.smith", - "verified": True + 'id': '123123123123', + 'first_name': 'John', + 'gender': 'male', + 'last_name': 'Smith', + 'link': 'https://www.facebook.com/john.smith', + 'locale': 'en_US', + 'name': 'John Smith', + 'timezone': 2, + 'updated_time': '2014-08-13T10:14:38+0000', + 'username': 'john.smith', + 'verified': True, } responses.add( @@ -389,18 +390,18 @@ def test_social_connect(self): self.graph_api_url, body=json.dumps(resp_body), status=200, - content_type='application/json' + content_type='application/json', ) payload = { - 'access_token': 'abc123' + 'access_token': 'abc123', } self.post(self.fb_connect_url, data=payload, status_code=200) self.assertIn('key', self.response.json.keys()) # Test Twitter resp_body = { - "id": "123123123123", + 'id': '123123123123', } responses.add( @@ -408,12 +409,12 @@ def test_social_connect(self): self.twitter_url, body=json.dumps(resp_body), status=200, - content_type='application/json' + content_type='application/json', ) payload = { 'access_token': 'abc123', - 'token_secret': '1111222233334444' + 'token_secret': '1111222233334444', } self.post(self.tw_connect_url, data=payload) @@ -430,12 +431,12 @@ def test_social_connect(self): # Try disconnecting accounts self.incorrect_disconnect_url = reverse( - 'social_account_disconnect', args=[999999999] + 'social_account_disconnect', args=[999999999], ) self.post(self.incorrect_disconnect_url, status_code=404) self.disconnect_url = reverse( - 'social_account_disconnect', args=[facebook_social_account_id] + 'social_account_disconnect', args=[facebook_social_account_id], ) self.post(self.disconnect_url, status_code=200) diff --git a/dj_rest_auth/tests/urls.py b/dj_rest_auth/tests/urls.py index c7533564..ba2e1cc4 100644 --- a/dj_rest_auth/tests/urls.py +++ b/dj_rest_auth/tests/urls.py @@ -1,5 +1,4 @@ -from allauth.socialaccount.providers.facebook.views import \ - FacebookOAuth2Adapter +from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter from django.conf.urls import include, url from django.views.decorators.csrf import ensure_csrf_cookie @@ -11,12 +10,13 @@ from rest_framework_simplejwt.views import TokenVerifyView from dj_rest_auth.jwt_auth import get_refresh_view -from dj_rest_auth.registration.views import (SocialAccountDisconnectView, - SocialAccountListView, - SocialConnectView, - SocialLoginView) -from dj_rest_auth.social_serializers import (TwitterConnectSerializer, - TwitterLoginSerializer) +from dj_rest_auth.registration.views import ( + SocialAccountDisconnectView, SocialAccountListView, SocialConnectView, + SocialLoginView, +) +from dj_rest_auth.social_serializers import ( + TwitterConnectSerializer, TwitterLoginSerializer, +) from dj_rest_auth.urls import urlpatterns from . import django_urls @@ -58,7 +58,7 @@ class TwitterLoginSerializerFoo(TwitterLoginSerializer): def twitter_login_view(request): serializer = TwitterLoginSerializerFoo( data={'access_token': '11223344', 'token_secret': '55667788'}, - context={'request': request} + context={'request': request}, ) serializer.is_valid(raise_exception=True) @@ -76,10 +76,14 @@ def get_csrf_cookie(request): urlpatterns += [ url(r'^rest-registration/', include('dj_rest_auth.registration.urls')), url(r'^test-admin/', include(django_urls)), - url(r'^account-email-verification-sent/$', TemplateView.as_view(), - name='account_email_verification_sent'), - url(r'^account-confirm-email/(?P[-:\w]+)/$', TemplateView.as_view(), - name='account_confirm_email'), + url( + r'^account-email-verification-sent/$', TemplateView.as_view(), + name='account_email_verification_sent', + ), + url( + r'^account-confirm-email/(?P[-:\w]+)/$', TemplateView.as_view(), + name='account_confirm_email', + ), url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'), url(r'^social-login/twitter/$', TwitterLogin.as_view(), name='tw_login'), url(r'^social-login/twitter-no-view/$', twitter_login_view, name='tw_login_no_view'), @@ -88,10 +92,12 @@ def get_csrf_cookie(request): url(r'^social-login/twitter/connect/$', TwitterConnect.as_view(), name='tw_connect'), url(r'^socialaccounts/$', SocialAccountListView.as_view(), name='social_account_list'), url(r'^protected-view/$', ExampleProtectedView.as_view()), - url(r'^socialaccounts/(?P\d+)/disconnect/$', SocialAccountDisconnectView.as_view(), - name='social_account_disconnect'), + url( + r'^socialaccounts/(?P\d+)/disconnect/$', SocialAccountDisconnectView.as_view(), + name='social_account_disconnect', + ), url(r'^accounts/', include('allauth.socialaccount.urls')), url(r'^getcsrf/', get_csrf_cookie, name='getcsrf'), url('^token/verify/', TokenVerifyView.as_view(), name='token_verify'), url('^token/refresh/', get_refresh_view().as_view(), name='token_refresh'), -] \ No newline at end of file +] diff --git a/dj_rest_auth/urls.py b/dj_rest_auth/urls.py index 7da0d13b..6429a7b8 100644 --- a/dj_rest_auth/urls.py +++ b/dj_rest_auth/urls.py @@ -1,9 +1,11 @@ from django.conf import settings from django.urls import path -from dj_rest_auth.views import (LoginView, LogoutView, PasswordChangeView, - PasswordResetConfirmView, PasswordResetView, - UserDetailsView) +from dj_rest_auth.views import ( + LoginView, LogoutView, PasswordChangeView, PasswordResetConfirmView, + PasswordResetView, UserDetailsView, +) + urlpatterns = [ # URLs that do not require a session or valid token diff --git a/dj_rest_auth/utils.py b/dj_rest_auth/utils.py index 4ecfbc11..dabb1736 100644 --- a/dj_rest_auth/utils.py +++ b/dj_rest_auth/utils.py @@ -23,7 +23,7 @@ def jwt_encode(user): JWTTokenClaimsSerializer = rest_auth_serializers.get( 'JWT_TOKEN_CLAIMS_SERIALIZER', - TokenObtainPairSerializer + TokenObtainPairSerializer, ) TOPS = import_callable(JWTTokenClaimsSerializer) diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index 60bf4331..dad44154 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -2,8 +2,8 @@ from django.contrib.auth import get_user_model from django.contrib.auth import login as django_login from django.contrib.auth import logout as django_logout -from django.utils import timezone from django.core.exceptions import ObjectDoesNotExist +from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.decorators.debug import sensitive_post_parameters @@ -13,18 +13,20 @@ from rest_framework.response import Response from rest_framework.views import APIView -from .app_settings import (JWTSerializer, JWTSerializerWithExpiration, LoginSerializer, - PasswordChangeSerializer, - PasswordResetConfirmSerializer, - PasswordResetSerializer, TokenSerializer, - UserDetailsSerializer, create_token) +from .app_settings import ( + JWTSerializer, JWTSerializerWithExpiration, LoginSerializer, + PasswordChangeSerializer, PasswordResetConfirmSerializer, + PasswordResetSerializer, TokenSerializer, UserDetailsSerializer, + create_token, +) from .models import TokenModel from .utils import jwt_encode + sensitive_post_parameters_m = method_decorator( sensitive_post_parameters( - 'password', 'old_password', 'new_password1', 'new_password2' - ) + 'password', 'old_password', 'new_password1', 'new_password2', + ), ) @@ -43,9 +45,13 @@ class LoginView(GenericAPIView): token_model = TokenModel throttle_scope = 'dj_rest_auth' + user = None + access_token = None + token = None + @sensitive_post_parameters_m def dispatch(self, *args, **kwargs): - return super(LoginView, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) def process_login(self): django_login(self.request, self.user) @@ -68,8 +74,10 @@ def login(self): if getattr(settings, 'REST_USE_JWT', False): self.access_token, self.refresh_token = jwt_encode(self.user) else: - self.token = create_token(self.token_model, self.user, - self.serializer) + self.token = create_token( + self.token_model, self.user, + self.serializer, + ) if getattr(settings, 'REST_SESSION_LOGIN', True): self.process_login() @@ -77,10 +85,10 @@ def login(self): def get_response(self): serializer_class = self.get_response_serializer() - access_token_expiration = None - refresh_token_expiration = None if getattr(settings, 'REST_USE_JWT', False): - from rest_framework_simplejwt.settings import api_settings as jwt_settings + from rest_framework_simplejwt.settings import ( + api_settings as jwt_settings, + ) access_token_expiration = (timezone.now() + jwt_settings.ACCESS_TOKEN_LIFETIME) refresh_token_expiration = (timezone.now() + jwt_settings.REFRESH_TOKEN_LIFETIME) return_expiration_times = getattr(settings, 'JWT_AUTH_RETURN_EXPIRATION', False) @@ -88,18 +96,22 @@ def get_response(self): data = { 'user': self.user, 'access_token': self.access_token, - 'refresh_token': self.refresh_token + 'refresh_token': self.refresh_token, } if return_expiration_times: data['access_token_expiration'] = access_token_expiration data['refresh_token_expiration'] = refresh_token_expiration - serializer = serializer_class(instance=data, - context=self.get_serializer_context()) + serializer = serializer_class( + instance=data, + context=self.get_serializer_context(), + ) else: - serializer = serializer_class(instance=self.token, - context=self.get_serializer_context()) + serializer = serializer_class( + instance=self.token, + context=self.get_serializer_context(), + ) response = Response(serializer.data, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): @@ -147,8 +159,8 @@ def logout(self, request): django_logout(request) response = Response( - {"detail": _("Successfully logged out.")}, - status=status.HTTP_200_OK + {'detail': _('Successfully logged out.')}, + status=status.HTTP_200_OK, ) if getattr(settings, 'REST_USE_JWT', False): @@ -157,6 +169,7 @@ def logout(self, request): # True we shouldn't need the dependency from rest_framework_simplejwt.exceptions import TokenError from rest_framework_simplejwt.tokens import RefreshToken + from .jwt_auth import unset_jwt_cookies cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) @@ -168,27 +181,27 @@ def logout(self, request): token = RefreshToken(request.data['refresh']) token.blacklist() except KeyError: - response.data = {"detail": _("Refresh token was not included in request data.")} + response.data = {'detail': _('Refresh token was not included in request data.')} response.status_code =status.HTTP_401_UNAUTHORIZED except (TokenError, AttributeError, TypeError) as error: if hasattr(error, 'args'): if 'Token is blacklisted' in error.args or 'Token is invalid or expired' in error.args: - response.data = {"detail": _(error.args[0])} + response.data = {'detail': _(error.args[0])} response.status_code = status.HTTP_401_UNAUTHORIZED else: - response.data = {"detail": _("An error has occurred.")} + response.data = {'detail': _('An error has occurred.')} response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR else: - response.data = {"detail": _("An error has occurred.")} + response.data = {'detail': _('An error has occurred.')} response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR elif not cookie_name: message = _( - "Neither cookies or blacklist are enabled, so the token " - "has not been deleted server side. Please make sure the token is deleted client side." + 'Neither cookies or blacklist are enabled, so the token ' + 'has not been deleted server side. Please make sure the token is deleted client side.', ) - response.data = {"detail": message} + response.data = {'detail': message} response.status_code = status.HTTP_200_OK return response @@ -237,8 +250,8 @@ def post(self, request, *args, **kwargs): serializer.save() # Return the success message with OK HTTP status return Response( - {"detail": _("Password reset e-mail has been sent.")}, - status=status.HTTP_200_OK + {'detail': _('Password reset e-mail has been sent.')}, + status=status.HTTP_200_OK, ) @@ -257,14 +270,14 @@ class PasswordResetConfirmView(GenericAPIView): @sensitive_post_parameters_m def dispatch(self, *args, **kwargs): - return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response( - {"detail": _("Password has been reset with the new password.")} + {'detail': _('Password has been reset with the new password.')}, ) @@ -281,10 +294,10 @@ class PasswordChangeView(GenericAPIView): @sensitive_post_parameters_m def dispatch(self, *args, **kwargs): - return super(PasswordChangeView, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() - return Response({"detail": _("New password has been saved.")}) + return Response({'detail': _('New password has been saved.')})