mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-05-29 02:03:19 +03:00
commit
0ac575e603
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -52,3 +52,4 @@ coverage.xml
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -3,11 +3,17 @@ python:
|
||||||
- "2.6"
|
- "2.6"
|
||||||
- "2.7"
|
- "2.7"
|
||||||
env:
|
env:
|
||||||
- DJANGO=1.5.8
|
- DJANGO=1.5.10
|
||||||
- DJANGO=1.6.5
|
- DJANGO=1.6.7
|
||||||
|
- DJANGO=1.7
|
||||||
|
matrix:
|
||||||
|
exclude:
|
||||||
|
- python: "2.6"
|
||||||
|
env: DJANGO=1.7
|
||||||
install:
|
install:
|
||||||
- pip install -q Django==$DJANGO --use-mirrors
|
- pip install -q Django==$DJANGO --use-mirrors
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
|
- pip install -r test_requirements.pip
|
||||||
script:
|
script:
|
||||||
- coverage run --source=rest_auth setup.py test
|
- coverage run --source=rest_auth setup.py test
|
||||||
after_success:
|
after_success:
|
||||||
|
|
41
rest_auth/app_settings.py
Normal file
41
rest_auth/app_settings.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from rest_auth.serializers import (
|
||||||
|
TokenSerializer as DefaultTokenSerializer,
|
||||||
|
UserDetailsSerializer as DefaultUserDetailsSerializer,
|
||||||
|
LoginSerializer as DefaultLoginSerializer,
|
||||||
|
PasswordResetSerializer as DefaultPasswordResetSerializer,
|
||||||
|
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
|
||||||
|
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
|
||||||
|
from .utils import import_callable
|
||||||
|
|
||||||
|
|
||||||
|
serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
|
||||||
|
|
||||||
|
TokenSerializer = import_callable(
|
||||||
|
serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer))
|
||||||
|
|
||||||
|
UserDetailsSerializer = import_callable(
|
||||||
|
serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
LoginSerializer = import_callable(
|
||||||
|
serializers.get('LOGIN_SERIALIZER', DefaultLoginSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
PasswordResetSerializer = import_callable(
|
||||||
|
serializers.get('PASSWORD_RESET_SERIALIZER',
|
||||||
|
DefaultPasswordResetSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
PasswordResetConfirmSerializer = import_callable(
|
||||||
|
serializers.get('PASSWORD_RESET_CONFIRM_SERIALIZER',
|
||||||
|
DefaultPasswordResetConfirmSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
PasswordChangeSerializer = import_callable(
|
||||||
|
serializers.get('PASSWORD_CHANGE_SERIALIZER',
|
||||||
|
DefaultPasswordChangeSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
0
rest_auth/registration/__init__.py
Normal file
0
rest_auth/registration/__init__.py
Normal file
43
rest_auth/registration/serializers.py
Normal file
43
rest_auth/registration/serializers.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
from allauth.socialaccount.helpers import complete_social_login
|
||||||
|
|
||||||
|
|
||||||
|
class SocialLoginSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
access_token = serializers.CharField(required=True)
|
||||||
|
|
||||||
|
def validate_access_token(self, attrs, source):
|
||||||
|
access_token = attrs[source]
|
||||||
|
|
||||||
|
view = self.context.get('view')
|
||||||
|
request = self.context.get('request')
|
||||||
|
|
||||||
|
if not view:
|
||||||
|
raise serializers.ValidationError('View is not defined, pass it ' +
|
||||||
|
'as a context variable')
|
||||||
|
self.adapter_class = getattr(view, 'adapter_class', None)
|
||||||
|
|
||||||
|
if not self.adapter_class:
|
||||||
|
raise serializers.ValidationError('Define adapter_class in view')
|
||||||
|
|
||||||
|
self.adapter = self.adapter_class()
|
||||||
|
app = self.adapter.get_provider().get_app(request)
|
||||||
|
token = self.adapter.parse_token({'access_token': access_token})
|
||||||
|
token.app = app
|
||||||
|
|
||||||
|
try:
|
||||||
|
login = self.adapter.complete_login(request, app, token,
|
||||||
|
response=access_token)
|
||||||
|
token.account = login.account
|
||||||
|
login.token = token
|
||||||
|
complete_social_login(request, login)
|
||||||
|
except HTTPError:
|
||||||
|
raise serializers.ValidationError('Incorrect value')
|
||||||
|
|
||||||
|
if not login.is_existing:
|
||||||
|
login.lookup()
|
||||||
|
login.save(request, connect=True)
|
||||||
|
self.object = {'user': login.account.user}
|
||||||
|
|
||||||
|
return attrs
|
16
rest_auth/registration/urls.py
Normal file
16
rest_auth/registration/urls.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from .views import Register, VerifyEmail
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
url(r'^$', Register.as_view(), name='rest_register'),
|
||||||
|
url(r'^verify-email/$', VerifyEmail.as_view(), name='verify_email'),
|
||||||
|
|
||||||
|
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
|
||||||
|
name='account_email_verification_sent'),
|
||||||
|
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
|
||||||
|
name='account_confirm_email'),
|
||||||
|
|
||||||
|
)
|
||||||
|
|
66
rest_auth/registration/views.py
Normal file
66
rest_auth/registration/views.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from allauth.account.views import SignupView, ConfirmEmailView
|
||||||
|
from allauth.account.utils import complete_signup
|
||||||
|
from allauth.account import app_settings
|
||||||
|
|
||||||
|
from rest_auth.serializers import UserDetailsSerializer
|
||||||
|
from rest_auth.registration.serializers import SocialLoginSerializer
|
||||||
|
from rest_auth.views import Login
|
||||||
|
|
||||||
|
|
||||||
|
class Register(APIView, SignupView):
|
||||||
|
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
user_serializer_class = UserDetailsSerializer
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
self.user = form.save(self.request)
|
||||||
|
return complete_signup(self.request, self.user,
|
||||||
|
app_settings.EMAIL_VERIFICATION,
|
||||||
|
self.get_success_url())
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.initial = {}
|
||||||
|
self.request.POST = self.request.DATA.copy()
|
||||||
|
form_class = self.get_form_class()
|
||||||
|
self.form = self.get_form(form_class)
|
||||||
|
if self.form.is_valid():
|
||||||
|
self.form_valid(self.form)
|
||||||
|
return self.get_response()
|
||||||
|
else:
|
||||||
|
return self.get_response_with_errors()
|
||||||
|
|
||||||
|
def get_response(self):
|
||||||
|
serializer = self.user_serializer_class(instance=self.user)
|
||||||
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def get_response_with_errors(self):
|
||||||
|
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyEmail(APIView, ConfirmEmailView):
|
||||||
|
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.kwargs['key'] = self.request.DATA.get('key', '')
|
||||||
|
confirmation = self.get_object()
|
||||||
|
confirmation.confirm(self.request)
|
||||||
|
return Response({'message': 'ok'}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class SocialLogin(Login):
|
||||||
|
"""
|
||||||
|
class used for social authentications
|
||||||
|
example usage for facebook
|
||||||
|
|
||||||
|
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||||
|
class FacebookLogin(SocialLogin):
|
||||||
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer_class = SocialLoginSerializer
|
|
@ -1,60 +1,21 @@
|
||||||
# This file mainly exists to allow python setup.py test to work.
|
# This file mainly exists to allow python setup.py test to work.
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings'
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings'
|
||||||
test_dir = os.path.dirname(__file__)
|
test_dir = os.path.dirname(__file__)
|
||||||
sys.path.insert(0, test_dir)
|
sys.path.insert(0, test_dir)
|
||||||
|
|
||||||
|
import django
|
||||||
from django.test.utils import get_runner
|
from django.test.utils import get_runner
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.sites.models import RequestSite
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from rest_framework.serializers import _resolve_model
|
|
||||||
from registration.models import RegistrationProfile
|
|
||||||
from registration.backends.default.views import RegistrationView as BaseRegistrationView
|
|
||||||
from registration import signals
|
|
||||||
|
|
||||||
"""
|
|
||||||
create user profile model
|
|
||||||
"""
|
|
||||||
class UserProfile(models.Model):
|
|
||||||
user = models.ForeignKey(User, unique=True)
|
|
||||||
newsletter_subscribe = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
app_label = 'rest_auth'
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
overwrite register to avoid sending email
|
|
||||||
"""
|
|
||||||
class RegistrationView(BaseRegistrationView):
|
|
||||||
def register(self, request, **cleaned_data):
|
|
||||||
username, email, password = cleaned_data['username'], cleaned_data['email'], cleaned_data['password1']
|
|
||||||
if Site._meta.installed:
|
|
||||||
site = Site.objects.get_current()
|
|
||||||
else:
|
|
||||||
site = RequestSite(request)
|
|
||||||
new_user = RegistrationProfile.objects.create_inactive_user(username, email,
|
|
||||||
password, site, send_email=False)
|
|
||||||
signals.user_registered.send(sender=self.__class__,
|
|
||||||
user=new_user,
|
|
||||||
request=request)
|
|
||||||
|
|
||||||
# create user profile
|
|
||||||
profile_model_path = getattr(settings, 'REST_PROFILE_MODULE', None)
|
|
||||||
if profile_model_path:
|
|
||||||
user_profile_model = _resolve_model(profile_model_path)
|
|
||||||
user_profile_model.objects.create(user=new_user)
|
|
||||||
|
|
||||||
return new_user
|
|
||||||
|
|
||||||
|
|
||||||
def runtests():
|
def runtests():
|
||||||
TestRunner = get_runner(settings)
|
TestRunner = get_runner(settings)
|
||||||
test_runner = TestRunner(verbosity=1, interactive=True)
|
test_runner = TestRunner(verbosity=1, interactive=True)
|
||||||
|
if hasattr(django, 'setup'):
|
||||||
|
django.setup()
|
||||||
failures = test_runner.run_tests(['rest_auth'])
|
failures = test_runner.run_tests(['rest_auth'])
|
||||||
sys.exit(bool(failures))
|
sys.exit(bool(failures))
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
||||||
|
try:
|
||||||
|
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
||||||
|
except:
|
||||||
|
# make compatible with django 1.5
|
||||||
|
from django.utils.http import base36_to_int as uid_decoder
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.serializers import _resolve_model
|
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||||
|
|
||||||
|
|
||||||
profile_model_path = lambda: getattr(settings, 'REST_PROFILE_MODULE', None)
|
class LoginSerializer(AuthTokenSerializer):
|
||||||
|
|
||||||
class LoginSerializer(serializers.Serializer):
|
def validate(self, attrs):
|
||||||
username = serializers.CharField(max_length=30)
|
attrs = super(LoginSerializer, self).validate(attrs)
|
||||||
password = serializers.CharField(max_length=128)
|
|
||||||
|
if 'rest_auth.registration' in settings.INSTALLED_APPS:
|
||||||
|
from allauth.account import app_settings
|
||||||
|
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
||||||
|
user = attrs['user']
|
||||||
|
email_address = user.emailaddress_set.get(email=user.email)
|
||||||
|
if not email_address.verified:
|
||||||
|
raise serializers.ValidationError('E-mail is not verified.')
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class TokenSerializer(serializers.ModelSerializer):
|
class TokenSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Serializer for Token model.
|
Serializer for Token model.
|
||||||
"""
|
"""
|
||||||
|
@ -34,120 +48,6 @@ class UserDetailsSerializer(serializers.ModelSerializer):
|
||||||
fields = ('username', 'email', 'first_name', 'last_name')
|
fields = ('username', 'email', 'first_name', 'last_name')
|
||||||
|
|
||||||
|
|
||||||
class UserRegistrationSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Serializer for Django User model and most of its fields.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = get_user_model()
|
|
||||||
fields = ('username', 'password', 'email', 'first_name', 'last_name')
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
ModelSerializer that allows fields argument to control fields
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
fields = kwargs.pop('fields', None)
|
|
||||||
|
|
||||||
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
if fields:
|
|
||||||
allowed = set(fields)
|
|
||||||
existing = set(self.fields.keys())
|
|
||||||
|
|
||||||
for field_name in existing - allowed:
|
|
||||||
self.fields.pop(field_name)
|
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateSerializer(DynamicFieldsModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
User model w/o username and password
|
|
||||||
"""
|
|
||||||
class Meta:
|
|
||||||
model = get_user_model()
|
|
||||||
fields = ('id', 'email', 'first_name', 'last_name')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_registration_profile_serializer(*args, **kwargs):
|
|
||||||
if profile_model_path():
|
|
||||||
class UserRegistrationProfileSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Serializer that includes all profile fields except for user fk / id.
|
|
||||||
"""
|
|
||||||
class Meta:
|
|
||||||
|
|
||||||
model = _resolve_model(profile_model_path())
|
|
||||||
fields = filter(lambda x: x != 'id' and x != 'user',
|
|
||||||
map(lambda x: x.name, model._meta.fields))
|
|
||||||
else:
|
|
||||||
class UserRegistrationProfileSerializer(serializers.Serializer):
|
|
||||||
pass
|
|
||||||
return UserRegistrationProfileSerializer
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_profile_serializer(*args, **kwargs):
|
|
||||||
if profile_model_path():
|
|
||||||
class UserProfileSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Serializer for UserProfile model.
|
|
||||||
"""
|
|
||||||
|
|
||||||
user = UserDetailsSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
# http://stackoverflow.com/questions/4881607/django-get-model-from-string
|
|
||||||
model = _resolve_model(profile_model_path())
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(UserProfileSerializer, self).__init__(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
class UserProfileSerializer(serializers.Serializer):
|
|
||||||
pass
|
|
||||||
return UserProfileSerializer
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_profile_update_serializer(*args, **kwargs):
|
|
||||||
if profile_model_path():
|
|
||||||
class UserProfileUpdateSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Serializer for updating User and UserProfile model.
|
|
||||||
"""
|
|
||||||
|
|
||||||
user = UserUpdateSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
# http://stackoverflow.com/questions/4881607/django-get-model-from-string
|
|
||||||
model = _resolve_model(profile_model_path())
|
|
||||||
else:
|
|
||||||
class UserProfileUpdateSerializer(serializers.Serializer):
|
|
||||||
pass
|
|
||||||
return UserProfileUpdateSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class SetPasswordSerializer(serializers.Serializer):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Serializer for changing Django User password.
|
|
||||||
"""
|
|
||||||
|
|
||||||
new_password1 = serializers.CharField(max_length=128)
|
|
||||||
new_password2 = serializers.CharField(max_length=128)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.user = kwargs.pop('user', None)
|
|
||||||
return super(SetPasswordSerializer, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetSerializer(serializers.Serializer):
|
class PasswordResetSerializer(serializers.Serializer):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -155,3 +55,86 @@ class PasswordResetSerializer(serializers.Serializer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
email = serializers.EmailField()
|
email = serializers.EmailField()
|
||||||
|
|
||||||
|
password_reset_form_class = PasswordResetForm
|
||||||
|
|
||||||
|
def validate_email(self, attrs, source):
|
||||||
|
# Create PasswordResetForm with the serializer
|
||||||
|
self.reset_form = self.password_reset_form_class(data=attrs)
|
||||||
|
if not self.reset_form.is_valid():
|
||||||
|
raise serializers.ValidationError('Error')
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
request = self.context.get('request')
|
||||||
|
# Set some values to trigger the send_email method.
|
||||||
|
opts = {
|
||||||
|
'use_https': request.is_secure(),
|
||||||
|
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
|
||||||
|
'request': request,
|
||||||
|
}
|
||||||
|
self.reset_form.save(**opts)
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetConfirmSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Serializer for requesting a password reset e-mail.
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_password1 = serializers.CharField(max_length=128)
|
||||||
|
new_password2 = serializers.CharField(max_length=128)
|
||||||
|
|
||||||
|
uid = serializers.CharField(required=True)
|
||||||
|
token = serializers.CharField(required=True)
|
||||||
|
|
||||||
|
set_password_form_class = SetPasswordForm
|
||||||
|
|
||||||
|
def custom_validation(self, attrs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
self._errors = {}
|
||||||
|
# Get the UserModel
|
||||||
|
UserModel = get_user_model()
|
||||||
|
# Decode the uidb64 to uid to get User object
|
||||||
|
try:
|
||||||
|
uid = uid_decoder(attrs['uid'])
|
||||||
|
self.user = UserModel._default_manager.get(pk=uid)
|
||||||
|
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
|
||||||
|
self._errors['uid'] = ['Invalid value']
|
||||||
|
|
||||||
|
self.custom_validation(attrs)
|
||||||
|
|
||||||
|
# Construct SetPasswordForm instance
|
||||||
|
self.set_password_form = self.set_password_form_class(user=self.user,
|
||||||
|
data=attrs)
|
||||||
|
if not self.set_password_form.is_valid():
|
||||||
|
self._errors['token'] = ['Invalid value']
|
||||||
|
|
||||||
|
if not default_token_generator.check_token(self.user, attrs['token']):
|
||||||
|
self._errors['token'] = ['Invalid value']
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.set_password_form.save()
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordChangeSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
new_password1 = serializers.CharField(max_length=128)
|
||||||
|
new_password2 = serializers.CharField(max_length=128)
|
||||||
|
|
||||||
|
set_password_form_class = SetPasswordForm
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
request = self.context.get('request')
|
||||||
|
self.set_password_form = self.set_password_form_class(user=request.user,
|
||||||
|
data=attrs)
|
||||||
|
|
||||||
|
if not self.set_password_form.is_valid():
|
||||||
|
self._errors = self.set_password_form.errors
|
||||||
|
return None
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.set_password_form.save()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import django
|
import django
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0])
|
PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0])
|
||||||
ROOT_URLCONF = 'urls'
|
ROOT_URLCONF = 'urls'
|
||||||
|
@ -27,6 +28,26 @@ if django.VERSION[:2] >= (1, 3):
|
||||||
else:
|
else:
|
||||||
DATABASE_ENGINE = 'sqlite3'
|
DATABASE_ENGINE = 'sqlite3'
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = [
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware'
|
||||||
|
]
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = [
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.request',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'django.core.context_processors.static',
|
||||||
|
|
||||||
|
"allauth.account.context_processors.account",
|
||||||
|
"allauth.socialaccount.context_processors.socialaccount",
|
||||||
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
|
@ -37,13 +58,22 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sitemaps',
|
'django.contrib.sitemaps',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
|
'allauth',
|
||||||
|
'allauth.account',
|
||||||
|
'allauth.socialaccount',
|
||||||
|
'allauth.socialaccount.providers.facebook',
|
||||||
|
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'registration',
|
|
||||||
|
|
||||||
'rest_auth',
|
'rest_auth',
|
||||||
|
'rest_auth.registration'
|
||||||
]
|
]
|
||||||
|
|
||||||
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
|
||||||
ACCOUNT_ACTIVATION_DAYS = 1
|
ACCOUNT_ACTIVATION_DAYS = 1
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
MIGRATION_MODULES = {
|
||||||
|
'authtoken': 'authtoken.migrations',
|
||||||
|
}
|
||||||
|
|
22
rest_auth/test_urls.py
Normal file
22
rest_auth/test_urls.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from django.conf.urls import patterns, url, include
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
from django.contrib.auth.tests import urls
|
||||||
|
|
||||||
|
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||||
|
|
||||||
|
from .urls import urlpatterns
|
||||||
|
from .registration.views import SocialLogin
|
||||||
|
|
||||||
|
|
||||||
|
class FacebookLogin(SocialLogin):
|
||||||
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
url(r'^rest-registration/', include('registration.urls')),
|
||||||
|
url(r'^test-admin/', include(urls)),
|
||||||
|
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
|
||||||
|
name='account_email_verification_sent'),
|
||||||
|
url(r'^account-confirm-email/(?P<key>\w+)/$', TemplateView.as_view(),
|
||||||
|
name='account_confirm_email'),
|
||||||
|
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login')
|
||||||
|
)
|
|
@ -8,9 +8,13 @@ from django.test import TestCase
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
from registration.models import RegistrationProfile
|
from allauth.socialaccount.models import SocialApp
|
||||||
from rest_framework.serializers import _resolve_model
|
import responses
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
|
||||||
class APIClient(Client):
|
class APIClient(Client):
|
||||||
|
@ -22,16 +26,16 @@ class APIClient(Client):
|
||||||
return self.generic('OPTIONS', path, data, content_type, **extra)
|
return self.generic('OPTIONS', path, data, content_type, **extra)
|
||||||
|
|
||||||
|
|
||||||
class CustomJSONEncoder(json.JSONEncoder):
|
# class CustomJSONEncoder(json.JSONEncoder):
|
||||||
"""
|
# """
|
||||||
Convert datetime/date objects into isoformat
|
# Convert datetime/date objects into isoformat
|
||||||
"""
|
# """
|
||||||
|
|
||||||
def default(self, obj):
|
# def default(self, obj):
|
||||||
if isinstance(obj, (datetime, date, time)):
|
# if isinstance(obj, (datetime, date, time)):
|
||||||
return obj.isoformat()
|
# return obj.isoformat()
|
||||||
else:
|
# else:
|
||||||
return super(CustomJSONEncoder, self).default(obj)
|
# return super(CustomJSONEncoder, self).default(obj)
|
||||||
|
|
||||||
|
|
||||||
class BaseAPITestCase(object):
|
class BaseAPITestCase(object):
|
||||||
|
@ -48,7 +52,7 @@ class BaseAPITestCase(object):
|
||||||
kwargs['content_type'] = 'application/json'
|
kwargs['content_type'] = 'application/json'
|
||||||
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
|
if 'data' in kwargs and request_method != 'get' and kwargs['content_type'] == 'application/json':
|
||||||
data = kwargs.get('data', '')
|
data = kwargs.get('data', '')
|
||||||
kwargs['data'] = json.dumps(data, cls=CustomJSONEncoder)
|
kwargs['data'] = json.dumps(data) # , cls=CustomJSONEncoder
|
||||||
if 'status_code' in kwargs:
|
if 'status_code' in kwargs:
|
||||||
status_code = kwargs.pop('status_code')
|
status_code = kwargs.pop('status_code')
|
||||||
|
|
||||||
|
@ -56,10 +60,6 @@ class BaseAPITestCase(object):
|
||||||
if hasattr(self, 'token'):
|
if hasattr(self, 'token'):
|
||||||
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
|
kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token
|
||||||
|
|
||||||
if hasattr(self, 'company_token'):
|
|
||||||
kwargs[
|
|
||||||
'HTTP_AUTHORIZATION'] = 'Company-Token %s' % self.company_token
|
|
||||||
|
|
||||||
self.response = request_func(*args, **kwargs)
|
self.response = request_func(*args, **kwargs)
|
||||||
is_json = bool(
|
is_json = bool(
|
||||||
filter(lambda x: 'json' in x, self.response._headers['content-type']))
|
filter(lambda x: 'json' in x, self.response._headers['content-type']))
|
||||||
|
@ -80,28 +80,28 @@ class BaseAPITestCase(object):
|
||||||
def patch(self, *args, **kwargs):
|
def patch(self, *args, **kwargs):
|
||||||
return self.send_request('patch', *args, **kwargs)
|
return self.send_request('patch', *args, **kwargs)
|
||||||
|
|
||||||
def put(self, *args, **kwargs):
|
# def put(self, *args, **kwargs):
|
||||||
return self.send_request('put', *args, **kwargs)
|
# return self.send_request('put', *args, **kwargs)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
# def delete(self, *args, **kwargs):
|
||||||
return self.send_request('delete', *args, **kwargs)
|
# return self.send_request('delete', *args, **kwargs)
|
||||||
|
|
||||||
def options(self, *args, **kwargs):
|
# def options(self, *args, **kwargs):
|
||||||
return self.send_request('options', *args, **kwargs)
|
# return self.send_request('options', *args, **kwargs)
|
||||||
|
|
||||||
def post_file(self, *args, **kwargs):
|
# def post_file(self, *args, **kwargs):
|
||||||
kwargs['content_type'] = MULTIPART_CONTENT
|
# kwargs['content_type'] = MULTIPART_CONTENT
|
||||||
return self.send_request('post', *args, **kwargs)
|
# return self.send_request('post', *args, **kwargs)
|
||||||
|
|
||||||
def get_file(self, *args, **kwargs):
|
# def get_file(self, *args, **kwargs):
|
||||||
content_type = None
|
# content_type = None
|
||||||
if 'content_type' in kwargs:
|
# if 'content_type' in kwargs:
|
||||||
content_type = kwargs.pop('content_type')
|
# content_type = kwargs.pop('content_type')
|
||||||
response = self.send_request('get', *args, **kwargs)
|
# response = self.send_request('get', *args, **kwargs)
|
||||||
if content_type:
|
# if content_type:
|
||||||
self.assertEqual(
|
# self.assertEqual(
|
||||||
bool(filter(lambda x: content_type in x, response._headers['content-type'])), True)
|
# bool(filter(lambda x: content_type in x, response._headers['content-type'])), True)
|
||||||
return response
|
# return response
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
settings.DEBUG = True
|
settings.DEBUG = True
|
||||||
|
@ -113,7 +113,6 @@ class BaseAPITestCase(object):
|
||||||
# -----------------------
|
# -----------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class APITestCase1(TestCase, BaseAPITestCase):
|
class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
"""
|
"""
|
||||||
Case #1:
|
Case #1:
|
||||||
|
@ -121,23 +120,23 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
- custom registration: backend defined
|
- custom registration: backend defined
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
urls = 'rest_auth.test_urls'
|
||||||
|
|
||||||
USERNAME = 'person'
|
USERNAME = 'person'
|
||||||
PASS = 'person'
|
PASS = 'person'
|
||||||
EMAIL = "person1@world.com"
|
EMAIL = "person1@world.com"
|
||||||
NEW_PASS = 'new-test-pass'
|
NEW_PASS = 'new-test-pass'
|
||||||
PROFILE_MODEL = 'rest_auth.UserProfile'
|
|
||||||
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
|
REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView'
|
||||||
|
|
||||||
# data without user profile
|
# data without user profile
|
||||||
BASIC_REGISTRATION_DATA = {
|
REGISTRATION_DATA = {
|
||||||
"username": USERNAME,
|
"username": USERNAME,
|
||||||
"password": PASS,
|
"password1": PASS,
|
||||||
"email": EMAIL
|
"password2": PASS
|
||||||
}
|
}
|
||||||
|
|
||||||
# data with user profile
|
REGISTRATION_DATA_WITH_EMAIL = REGISTRATION_DATA.copy()
|
||||||
REGISTRATION_DATA = BASIC_REGISTRATION_DATA.copy()
|
REGISTRATION_DATA_WITH_EMAIL['email'] = EMAIL
|
||||||
REGISTRATION_DATA['newsletter_subscribe'] = False
|
|
||||||
|
|
||||||
BASIC_USER_DATA = {
|
BASIC_USER_DATA = {
|
||||||
'first_name': "John",
|
'first_name': "John",
|
||||||
|
@ -150,28 +149,44 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.init()
|
self.init()
|
||||||
self.login_url = reverse('rest_login')
|
self.login_url = reverse('rest_login')
|
||||||
|
self.logout_url = reverse('rest_logout')
|
||||||
self.password_change_url = reverse('rest_password_change')
|
self.password_change_url = reverse('rest_password_change')
|
||||||
self.register_url = reverse('rest_register')
|
self.register_url = reverse('rest_register')
|
||||||
self.password_reset_url = reverse('rest_password_reset')
|
self.password_reset_url = reverse('rest_password_reset')
|
||||||
self.user_url = reverse('rest_user_details')
|
self.user_url = reverse('rest_user_details')
|
||||||
|
self.veirfy_email_url = reverse('verify_email')
|
||||||
|
|
||||||
setattr(settings, 'REST_PROFILE_MODULE', self.PROFILE_MODEL)
|
def _login(self):
|
||||||
self.user_profile_model = None
|
payload = {
|
||||||
if self.PROFILE_MODEL:
|
"username": self.USERNAME,
|
||||||
self.user_profile_model = _resolve_model(self.PROFILE_MODEL)
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK)
|
||||||
|
|
||||||
if self.REGISTRATION_VIEW:
|
def _logout(self):
|
||||||
setattr(settings, 'REST_REGISTRATION_BACKEND', self.REGISTRATION_VIEW)
|
self.post(self.logout_url, status=status.HTTP_200_OK)
|
||||||
elif hasattr(settings, 'REST_REGISTRATION_BACKEND'):
|
|
||||||
delattr(settings, 'REST_REGISTRATION_BACKEND')
|
def _generate_uid_and_token(self, user):
|
||||||
|
result = {}
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django import VERSION
|
||||||
|
if VERSION[1] == 5:
|
||||||
|
from django.utils.http import int_to_base36
|
||||||
|
result['uid'] = int_to_base36(user.pk)
|
||||||
|
else:
|
||||||
|
from django.utils.http import urlsafe_base64_encode
|
||||||
|
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
|
||||||
|
result['token'] = default_token_generator.make_token(user)
|
||||||
|
return result
|
||||||
|
|
||||||
def test_login(self):
|
def test_login(self):
|
||||||
payload = {
|
payload = {
|
||||||
"username": self.USERNAME,
|
"username": self.USERNAME,
|
||||||
"password": self.PASS
|
"password": self.PASS
|
||||||
}
|
}
|
||||||
# there is no users in db so it should throw error (401)
|
# there is no users in db so it should throw error (400)
|
||||||
self.post(self.login_url, data=payload, status_code=401)
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
self.post(self.password_change_url, status_code=403)
|
self.post(self.password_change_url, status_code=403)
|
||||||
|
|
||||||
|
@ -187,25 +202,24 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# test inactive user
|
# test inactive user
|
||||||
user.is_active = False
|
user.is_active = False
|
||||||
user.save()
|
user.save()
|
||||||
self.post(self.login_url, data=payload, status_code=401)
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
# test wrong username/password
|
# test wrong username/password
|
||||||
payload = {
|
payload = {
|
||||||
"username": self.USERNAME + '?',
|
"username": self.USERNAME + '?',
|
||||||
"password": self.PASS
|
"password": self.PASS
|
||||||
}
|
}
|
||||||
self.post(self.login_url, data=payload, status_code=401)
|
self.post(self.login_url, data=payload, status_code=400)
|
||||||
|
|
||||||
# test empty payload
|
# test empty payload
|
||||||
self.post(self.login_url, data={}, status_code=400)
|
self.post(self.login_url, data={}, status_code=400)
|
||||||
|
|
||||||
|
|
||||||
def test_password_change(self):
|
def test_password_change(self):
|
||||||
login_payload = {
|
login_payload = {
|
||||||
"username": self.USERNAME,
|
"username": self.USERNAME,
|
||||||
"password": self.PASS
|
"password": self.PASS
|
||||||
}
|
}
|
||||||
user = User.objects.create_user(self.USERNAME, '', self.PASS)
|
User.objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
self.post(self.login_url, data=login_payload, status_code=200)
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
self.token = self.response.json['key']
|
self.token = self.response.json['key']
|
||||||
|
|
||||||
|
@ -217,7 +231,7 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
status_code=200)
|
status_code=200)
|
||||||
|
|
||||||
# user should not be able to login using old password
|
# user should not be able to login using old password
|
||||||
self.post(self.login_url, data=login_payload, status_code=401)
|
self.post(self.login_url, data=login_payload, status_code=400)
|
||||||
|
|
||||||
# new password should work
|
# new password should work
|
||||||
login_payload['password'] = new_password_payload['new_password1']
|
login_payload['password'] = new_password_payload['new_password1']
|
||||||
|
@ -234,77 +248,23 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
# send empty payload
|
# send empty payload
|
||||||
self.post(self.password_change_url, data={}, status_code=400)
|
self.post(self.password_change_url, data={}, status_code=400)
|
||||||
|
|
||||||
def test_registration(self):
|
|
||||||
user_count = User.objects.all().count()
|
|
||||||
|
|
||||||
# test empty payload
|
|
||||||
self.post(self.register_url, data={}, status_code=400)
|
|
||||||
|
|
||||||
self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
|
|
||||||
self.assertEqual(User.objects.all().count(), user_count+1)
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
|
||||||
|
|
||||||
if self.REGISTRATION_VIEW:
|
|
||||||
activation_key = RegistrationProfile.objects.latest('id').activation_key
|
|
||||||
verify_url = reverse('verify_email',
|
|
||||||
kwargs={'activation_key': activation_key})
|
|
||||||
|
|
||||||
# new user at this point shouldn't be active
|
|
||||||
self.assertEqual(new_user.is_active, False)
|
|
||||||
|
|
||||||
# let's active new user and check is_active flag
|
|
||||||
self.get(verify_url)
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
|
||||||
self.assertEqual(new_user.is_active, True)
|
|
||||||
if self.user_profile_model:
|
|
||||||
user_profile = self.user_profile_model.objects.get(user=new_user)
|
|
||||||
self.assertIsNotNone(user_profile)
|
|
||||||
else:
|
|
||||||
self.assertEqual(new_user.is_active, True)
|
|
||||||
|
|
||||||
def test_registration_without_profile_data(self):
|
|
||||||
user_count = User.objects.all().count()
|
|
||||||
|
|
||||||
self.post(self.register_url, data=self.BASIC_REGISTRATION_DATA,
|
|
||||||
status_code=201)
|
|
||||||
self.assertEqual(User.objects.all().count(), user_count+1)
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
|
||||||
|
|
||||||
if self.REGISTRATION_VIEW:
|
|
||||||
activation_key = RegistrationProfile.objects.latest('id').activation_key
|
|
||||||
verify_url = reverse('verify_email',
|
|
||||||
kwargs={'activation_key': activation_key})
|
|
||||||
|
|
||||||
# new user at this point shouldn't be active
|
|
||||||
self.assertEqual(new_user.is_active, False)
|
|
||||||
|
|
||||||
# let's active new user and check is_active flag
|
|
||||||
self.get(verify_url)
|
|
||||||
new_user = get_user_model().objects.latest('id')
|
|
||||||
self.assertEqual(new_user.is_active, True)
|
|
||||||
if self.user_profile_model:
|
|
||||||
user_profile = self.user_profile_model.objects.get(user=new_user)
|
|
||||||
self.assertIsNotNone(user_profile)
|
|
||||||
else:
|
|
||||||
self.assertEqual(new_user.is_active, True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_password_reset(self):
|
def test_password_reset(self):
|
||||||
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
|
|
||||||
# call password reset
|
# call password reset
|
||||||
mail_count = len(mail.outbox)
|
mail_count = len(mail.outbox)
|
||||||
payload = {'email': self.EMAIL}
|
payload = {'email': self.EMAIL}
|
||||||
self.post(self.password_reset_url, data=payload)
|
self.post(self.password_reset_url, data=payload, status_code=200)
|
||||||
self.assertEqual(len(mail.outbox), mail_count + 1)
|
self.assertEqual(len(mail.outbox), mail_count + 1)
|
||||||
|
|
||||||
url_kwargs = self.generate_uid_and_token(user)
|
url_kwargs = self._generate_uid_and_token(user)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'new_password1': self.NEW_PASS,
|
'new_password1': self.NEW_PASS,
|
||||||
'new_password2': self.NEW_PASS
|
'new_password2': self.NEW_PASS,
|
||||||
|
'uid': url_kwargs['uid'],
|
||||||
|
'token': url_kwargs['token']
|
||||||
}
|
}
|
||||||
url = reverse('rest_password_reset_confirm', kwargs=url_kwargs)
|
url = reverse('rest_password_reset_confirm')
|
||||||
self.post(url, data=data, status_code=200)
|
self.post(url, data=data, status_code=200)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
|
@ -315,8 +275,6 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
|
|
||||||
def test_user_details(self):
|
def test_user_details(self):
|
||||||
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
if self.user_profile_model:
|
|
||||||
self.user_profile_model.objects.create(user=user)
|
|
||||||
payload = {
|
payload = {
|
||||||
"username": self.USERNAME,
|
"username": self.USERNAME,
|
||||||
"password": self.PASS
|
"password": self.PASS
|
||||||
|
@ -325,60 +283,100 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
self.token = self.response.json['key']
|
self.token = self.response.json['key']
|
||||||
self.get(self.user_url, status_code=200)
|
self.get(self.user_url, status_code=200)
|
||||||
|
|
||||||
self.post(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
|
self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
|
||||||
user = User.objects.get(pk=user.pk)
|
user = User.objects.get(pk=user.pk)
|
||||||
|
|
||||||
if self.user_profile_model:
|
|
||||||
self.post(self.user_url, data=self.USER_DATA, status_code=200)
|
|
||||||
user = User.objects.get(pk=user.pk)
|
|
||||||
self.assertEqual(user.first_name, self.response.json['user']['first_name'])
|
|
||||||
self.assertEqual(user.last_name, self.response.json['user']['last_name'])
|
|
||||||
self.assertEqual(user.email, self.response.json['user']['email'])
|
|
||||||
self.assertIn('newsletter_subscribe', self.response.json)
|
|
||||||
else:
|
|
||||||
self.assertEqual(user.first_name, self.response.json['first_name'])
|
self.assertEqual(user.first_name, self.response.json['first_name'])
|
||||||
self.assertEqual(user.last_name, self.response.json['last_name'])
|
self.assertEqual(user.last_name, self.response.json['last_name'])
|
||||||
self.assertEqual(user.email, self.response.json['email'])
|
self.assertEqual(user.email, self.response.json['email'])
|
||||||
|
|
||||||
|
def test_registration(self):
|
||||||
|
user_count = User.objects.all().count()
|
||||||
|
|
||||||
def generate_uid_and_token(self, user):
|
# test empty payload
|
||||||
result = {}
|
self.post(self.register_url, data={}, status_code=400)
|
||||||
from django.utils.encoding import force_bytes
|
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
|
||||||
from django import VERSION
|
self.assertEqual(User.objects.all().count(), user_count + 1)
|
||||||
if VERSION[1] == 6:
|
new_user = get_user_model().objects.latest('id')
|
||||||
from django.utils.http import urlsafe_base64_encode
|
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
||||||
result['uid'] = urlsafe_base64_encode(force_bytes(user.pk))
|
|
||||||
elif VERSION[1] == 5:
|
self._login()
|
||||||
from django.utils.http import int_to_base36
|
self._logout()
|
||||||
result['uid'] = int_to_base36(user.pk)
|
|
||||||
result['token'] = default_token_generator.make_token(user)
|
@override_settings(
|
||||||
return result
|
ACCOUNT_EMAIL_VERIFICATION='mandatory',
|
||||||
|
ACCOUNT_EMAIL_REQUIRED=True
|
||||||
|
)
|
||||||
|
def test_registration_with_email_verification(self):
|
||||||
|
user_count = User.objects.all().count()
|
||||||
|
mail_count = len(mail.outbox)
|
||||||
|
|
||||||
|
# test empty payload
|
||||||
|
self.post(self.register_url, data={},
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
self.post(self.register_url, data=self.REGISTRATION_DATA_WITH_EMAIL,
|
||||||
|
status_code=status.HTTP_201_CREATED)
|
||||||
|
self.assertEqual(User.objects.all().count(), user_count + 1)
|
||||||
|
self.assertEqual(len(mail.outbox), mail_count + 1)
|
||||||
|
new_user = get_user_model().objects.latest('id')
|
||||||
|
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
|
||||||
|
|
||||||
|
# email is not verified yet
|
||||||
|
payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
self.post(self.login_url, data=payload,
|
||||||
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# veirfy email
|
||||||
|
email_confirmation = new_user.emailaddress_set.get(email=self.EMAIL)\
|
||||||
|
.emailconfirmation_set.order_by('-created')[0]
|
||||||
|
self.post(self.veirfy_email_url, data={"key": email_confirmation.key},
|
||||||
|
status_code=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
# try to login again
|
||||||
|
self._login()
|
||||||
|
self._logout()
|
||||||
|
|
||||||
|
|
||||||
class APITestCase2(APITestCase1):
|
class TestSocialAuth(TestCase, BaseAPITestCase):
|
||||||
"""
|
|
||||||
Case #2:
|
|
||||||
- user profile: not defined
|
|
||||||
- custom registration backend: not defined
|
|
||||||
"""
|
|
||||||
PROFILE_MODEL = None
|
|
||||||
|
|
||||||
|
urls = 'rest_auth.test_urls'
|
||||||
|
|
||||||
class APITestCase3(APITestCase1):
|
def setUp(self):
|
||||||
"""
|
social_app = SocialApp.objects.create(
|
||||||
Case #3:
|
provider='facebook',
|
||||||
- user profile: defined
|
name='Facebook',
|
||||||
- custom registration backend: not defined
|
client_id='123123123',
|
||||||
"""
|
secret='321321321',
|
||||||
REGISTRATION_VIEW = None
|
)
|
||||||
|
site = Site.objects.get_current()
|
||||||
|
social_app.sites.add(site)
|
||||||
|
self.fb_login_url = reverse('fb_login')
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_failed_social_auth(self):
|
||||||
|
# fake response
|
||||||
|
responses.add(responses.GET, 'https://graph.facebook.com/me',
|
||||||
|
body='', status=400, content_type='application/json')
|
||||||
|
|
||||||
class APITestCase4(APITestCase1):
|
payload = {
|
||||||
"""
|
'access_token': 'abc123'
|
||||||
Case #4:
|
}
|
||||||
- user profile: not defined
|
self.post(self.fb_login_url, data=payload, status_code=400)
|
||||||
- custom registration backend: not defined
|
|
||||||
"""
|
@responses.activate
|
||||||
PROFILE_MODEL = None
|
def test_social_auth(self):
|
||||||
REGISTRATION_VIEW = None
|
# 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}'
|
||||||
|
responses.add(responses.GET, 'https://graph.facebook.com/me',
|
||||||
|
body=resp_body, status=200, content_type='application/json')
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'access_token': 'abc123'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.post(self.fb_login_url, data=payload, status_code=200)
|
||||||
|
self.assertIn('key', self.response.json.keys())
|
||||||
|
|
|
@ -1,32 +1,18 @@
|
||||||
from django.conf import settings
|
from django.conf.urls import patterns, url
|
||||||
from django.conf.urls import patterns, url, include
|
|
||||||
|
|
||||||
from rest_auth.views import Login, Logout, Register, UserDetails, \
|
from rest_auth.views import (Login, Logout, UserDetails, PasswordChange,
|
||||||
PasswordChange, PasswordReset, VerifyEmail, PasswordResetConfirm
|
PasswordReset, PasswordResetConfirm)
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
urlpatterns = patterns('rest_auth.views',
|
|
||||||
# URLs that do not require a session or valid token
|
# URLs that do not require a session or valid token
|
||||||
url(r'^register/$', Register.as_view(),
|
|
||||||
name='rest_register'),
|
|
||||||
url(r'^password/reset/$', PasswordReset.as_view(),
|
url(r'^password/reset/$', PasswordReset.as_view(),
|
||||||
name='rest_password_reset'),
|
name='rest_password_reset'),
|
||||||
url(r'^password/reset/confirm/(?P<uid>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
|
url(r'^password/reset/confirm/$', PasswordResetConfirm.as_view(),
|
||||||
PasswordResetConfirm.as_view(
|
name='rest_password_reset_confirm'),
|
||||||
), name='rest_password_reset_confirm'),
|
|
||||||
url(r'^login/$', Login.as_view(), name='rest_login'),
|
url(r'^login/$', Login.as_view(), name='rest_login'),
|
||||||
url(r'^verify-email/(?P<activation_key>\w+)/$',
|
# URLs that require a user to be logged in with a valid session / token.
|
||||||
VerifyEmail.as_view(), name='verify_email'),
|
|
||||||
|
|
||||||
# URLs that require a user to be logged in with a valid
|
|
||||||
# session / token.
|
|
||||||
url(r'^logout/$', Logout.as_view(), name='rest_logout'),
|
url(r'^logout/$', Logout.as_view(), name='rest_logout'),
|
||||||
url(r'^user/$', UserDetails.as_view(),
|
url(r'^user/$', UserDetails.as_view(), name='rest_user_details'),
|
||||||
name='rest_user_details'),
|
|
||||||
url(r'^password/change/$', PasswordChange.as_view(),
|
url(r'^password/change/$', PasswordChange.as_view(),
|
||||||
name='rest_password_change'),
|
name='rest_password_change'),
|
||||||
)
|
)
|
||||||
|
|
||||||
if getattr(settings, 'IS_TEST', False):
|
|
||||||
from django.contrib.auth.tests import urls
|
|
||||||
urlpatterns += patterns('', url(r'^test-admin/', include(urls)))
|
|
||||||
|
|
|
@ -1,43 +1,11 @@
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
|
|
||||||
HASH_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
def import_callable(path_or_callable):
|
||||||
|
if hasattr(path_or_callable, '__call__'):
|
||||||
|
return path_or_callable
|
||||||
|
else:
|
||||||
|
assert isinstance(path_or_callable, (str, unicode))
|
||||||
|
package, attr = path_or_callable.rsplit('.', 1)
|
||||||
|
return getattr(import_module(package), attr)
|
||||||
|
|
||||||
|
|
||||||
def generate_new_hash_with_length(length):
|
|
||||||
"""
|
|
||||||
Generates a random string with the alphanumerical character set and given length.
|
|
||||||
"""
|
|
||||||
return get_random_string(length, HASH_CHARACTERS)
|
|
||||||
|
|
||||||
|
|
||||||
# Based on http://stackoverflow.com/a/547867. Thanks! Credit goes to you!
|
|
||||||
def construct_modules_and_import(name):
|
|
||||||
"""
|
|
||||||
Grab the Python string to import
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Get all the components by dot notations
|
|
||||||
components = name.split('.')
|
|
||||||
module = ''
|
|
||||||
i = 1
|
|
||||||
|
|
||||||
# Construct the partial Python string except the last package name
|
|
||||||
for comp in components:
|
|
||||||
if i < len(components):
|
|
||||||
module += str(comp)
|
|
||||||
|
|
||||||
if i < (len(components) - 1):
|
|
||||||
module += '.'
|
|
||||||
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# Import the module from above python string
|
|
||||||
mod = __import__(module)
|
|
||||||
|
|
||||||
# Import the component recursivcely
|
|
||||||
for comp in components[1:]:
|
|
||||||
mod = getattr(mod, comp)
|
|
||||||
|
|
||||||
# Return the imported module's class
|
|
||||||
return mod
|
|
||||||
|
|
|
@ -1,53 +1,19 @@
|
||||||
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
from django.contrib.auth import login, logout
|
||||||
from django.contrib.auth import authenticate, login, logout, get_user_model
|
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
|
||||||
try:
|
|
||||||
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
|
||||||
except:
|
|
||||||
# make compatible with django 1.5
|
|
||||||
from django.utils.http import base36_to_int as uid_decoder
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.generics import GenericAPIView
|
from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.serializers import _resolve_model
|
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
from rest_framework.authentication import SessionAuthentication, \
|
from rest_framework.authentication import SessionAuthentication, \
|
||||||
TokenAuthentication
|
TokenAuthentication
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
from registration.models import RegistrationProfile
|
from app_settings import (TokenSerializer, UserDetailsSerializer,
|
||||||
from registration import signals
|
LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer,
|
||||||
from registration.views import ActivationView
|
PasswordChangeSerializer)
|
||||||
|
|
||||||
from rest_auth.utils import construct_modules_and_import
|
|
||||||
from rest_auth.models import *
|
|
||||||
from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer,
|
|
||||||
LoginSerializer, UserRegistrationSerializer,
|
|
||||||
SetPasswordSerializer, PasswordResetSerializer, UserUpdateSerializer,
|
|
||||||
get_user_registration_profile_serializer, get_user_profile_serializer,
|
|
||||||
get_user_profile_update_serializer)
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_profile_model():
|
|
||||||
# Get the UserProfile model from the setting value
|
|
||||||
user_profile_path = getattr(settings, 'REST_PROFILE_MODULE', None)
|
|
||||||
if user_profile_path:
|
|
||||||
setattr(settings, 'AUTH_PROFILE_MODULE', user_profile_path)
|
|
||||||
return _resolve_model(user_profile_path)
|
|
||||||
|
|
||||||
|
|
||||||
def get_registration_backend():
|
|
||||||
# Get the REST Registration Backend for django-registration
|
|
||||||
registration_backend = getattr(settings, 'REST_REGISTRATION_BACKEND',
|
|
||||||
'registration.backends.simple.views.RegistrationView')
|
|
||||||
|
|
||||||
# Get the REST REGISTRATION BACKEND class from the setting value via above
|
|
||||||
# method
|
|
||||||
return construct_modules_and_import(registration_backend)
|
|
||||||
|
|
||||||
|
|
||||||
class LoggedInRESTAPIView(APIView):
|
class LoggedInRESTAPIView(APIView):
|
||||||
|
@ -73,36 +39,34 @@ class Login(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
|
|
||||||
serializer_class = LoginSerializer
|
serializer_class = LoginSerializer
|
||||||
token_model = Token
|
token_model = Token
|
||||||
token_serializer = TokenSerializer
|
response_serializer = TokenSerializer
|
||||||
|
|
||||||
def post(self, request):
|
def get_serializer(self):
|
||||||
# Create a serializer with request.DATA
|
return self.serializer_class(data=self.request.DATA,
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
context={'request': self.request, 'view': self})
|
||||||
|
|
||||||
if serializer.is_valid():
|
def login(self):
|
||||||
# Authenticate the credentials by grabbing Django User object
|
self.user = self.serializer.object['user']
|
||||||
user = authenticate(username=serializer.data['username'],
|
self.token, created = self.token_model.objects.get_or_create(
|
||||||
password=serializer.data['password'])
|
user=self.user)
|
||||||
|
|
||||||
if user and user.is_authenticated():
|
|
||||||
if user.is_active:
|
|
||||||
if getattr(settings, 'REST_SESSION_LOGIN', True):
|
if getattr(settings, 'REST_SESSION_LOGIN', True):
|
||||||
login(request, user)
|
login(self.request, self.user)
|
||||||
|
|
||||||
# Return REST Token object with OK HTTP status
|
def get_response(self):
|
||||||
token, created = self.token_model.objects.get_or_create(user=user)
|
return Response(self.response_serializer(self.token).data,
|
||||||
return Response(self.token_serializer(token).data,
|
|
||||||
status=status.HTTP_200_OK)
|
status=status.HTTP_200_OK)
|
||||||
else:
|
|
||||||
return Response({'error': 'This account is disabled.'},
|
def get_error_response(self):
|
||||||
status=status.HTTP_401_UNAUTHORIZED)
|
return Response(self.serializer.errors,
|
||||||
else:
|
|
||||||
return Response({'error': 'Invalid Username/Password.'},
|
|
||||||
status=status.HTTP_401_UNAUTHORIZED)
|
|
||||||
else:
|
|
||||||
return Response(serializer.errors,
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.serializer = self.get_serializer()
|
||||||
|
if not self.serializer.is_valid():
|
||||||
|
return self.get_error_response()
|
||||||
|
self.login()
|
||||||
|
return self.get_response()
|
||||||
|
|
||||||
|
|
||||||
class Logout(LoggedInRESTAPIView):
|
class Logout(LoggedInRESTAPIView):
|
||||||
|
|
||||||
|
@ -113,7 +77,7 @@ class Logout(LoggedInRESTAPIView):
|
||||||
Accepts/Returns nothing.
|
Accepts/Returns nothing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request):
|
def post(self, request):
|
||||||
try:
|
try:
|
||||||
request.user.auth_token.delete()
|
request.user.auth_token.delete()
|
||||||
except:
|
except:
|
||||||
|
@ -125,54 +89,7 @@ class Logout(LoggedInRESTAPIView):
|
||||||
status=status.HTTP_200_OK)
|
status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class Register(LoggedOutRESTAPIView, GenericAPIView):
|
class UserDetails(LoggedInRESTAPIView, RetrieveUpdateAPIView):
|
||||||
|
|
||||||
"""
|
|
||||||
Registers a new Django User object by accepting required field values.
|
|
||||||
|
|
||||||
Accepts the following POST parameters:
|
|
||||||
Required: username, password, email
|
|
||||||
Optional: first_name & last_name for User object and UserProfile fields
|
|
||||||
Returns the newly created User object including REST Framework Token key.
|
|
||||||
"""
|
|
||||||
|
|
||||||
serializer_class = UserRegistrationSerializer
|
|
||||||
|
|
||||||
def get_profile_serializer_class(self):
|
|
||||||
return get_user_registration_profile_serializer()
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
# Create serializers with request.DATA
|
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
|
||||||
profile_serializer_class = self.get_profile_serializer_class()
|
|
||||||
profile_serializer = profile_serializer_class(data=request.DATA)
|
|
||||||
|
|
||||||
if serializer.is_valid() and profile_serializer.is_valid():
|
|
||||||
# Change the password key to password1 so that RESTRegistrationView
|
|
||||||
# can accept the data
|
|
||||||
serializer.data['password1'] = serializer.data.pop('password')
|
|
||||||
|
|
||||||
# TODO: Make this customizable backend via settings.
|
|
||||||
# Call RESTRegistrationView().register to create new Django User
|
|
||||||
# and UserProfile models
|
|
||||||
data = serializer.data.copy()
|
|
||||||
data.update(profile_serializer.data)
|
|
||||||
|
|
||||||
RESTRegistrationView = get_registration_backend()
|
|
||||||
RESTRegistrationView().register(request, **data)
|
|
||||||
|
|
||||||
# Return the User object with Created HTTP status
|
|
||||||
return Response(UserDetailsSerializer(serializer.data).data,
|
|
||||||
status=status.HTTP_201_CREATED)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response({
|
|
||||||
'user': serializer.errors,
|
|
||||||
'profile': profile_serializer.errors},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class UserDetails(LoggedInRESTAPIView, GenericAPIView):
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns User's details in JSON format.
|
Returns User's details in JSON format.
|
||||||
|
@ -183,50 +100,10 @@ class UserDetails(LoggedInRESTAPIView, GenericAPIView):
|
||||||
Optional: email, first_name, last_name and UserProfile fields
|
Optional: email, first_name, last_name and UserProfile fields
|
||||||
Returns the updated UserProfile and/or User object.
|
Returns the updated UserProfile and/or User object.
|
||||||
"""
|
"""
|
||||||
if get_user_profile_model():
|
serializer_class = UserDetailsSerializer
|
||||||
serializer_class = get_user_profile_update_serializer()
|
|
||||||
else:
|
|
||||||
serializer_class = UserUpdateSerializer
|
|
||||||
|
|
||||||
def get_profile_serializer_class(self):
|
def get_object(self):
|
||||||
return get_user_profile_serializer()
|
return self.request.user
|
||||||
|
|
||||||
def get_profile_update_serializer_class(self):
|
|
||||||
return get_user_profile_update_serializer()
|
|
||||||
|
|
||||||
def get(self, request):
|
|
||||||
# Create serializers with request.user and profile
|
|
||||||
user_profile_model = get_user_profile_model()
|
|
||||||
if user_profile_model:
|
|
||||||
profile_serializer_class = self.get_profile_serializer_class()
|
|
||||||
serializer = profile_serializer_class(request.user.get_profile())
|
|
||||||
else:
|
|
||||||
serializer = UserDetailsSerializer(request.user)
|
|
||||||
# Send the Return the User and its profile model with OK HTTP status
|
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
# Get the User object updater via this Serializer
|
|
||||||
user_profile_model = get_user_profile_model()
|
|
||||||
if user_profile_model:
|
|
||||||
profile_serializer_class = self.get_profile_update_serializer_class()
|
|
||||||
serializer = profile_serializer_class(request.user.get_profile(),
|
|
||||||
data=request.DATA, partial=True)
|
|
||||||
else:
|
|
||||||
serializer = UserUpdateSerializer(request.user, data=request.DATA,
|
|
||||||
partial=True)
|
|
||||||
|
|
||||||
if serializer.is_valid():
|
|
||||||
# Save UserProfileUpdateSerializer
|
|
||||||
serializer.save()
|
|
||||||
|
|
||||||
# Return the User object with OK HTTP status
|
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Return the UserProfileUpdateSerializer errors with Bad Request
|
|
||||||
# HTTP status
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordReset(LoggedOutRESTAPIView, GenericAPIView):
|
class PasswordReset(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
|
@ -239,38 +116,18 @@ class PasswordReset(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = PasswordResetSerializer
|
serializer_class = PasswordResetSerializer
|
||||||
password_reset_form_class = PasswordResetForm
|
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request, *args, **kwargs):
|
||||||
# Create a serializer with request.DATA
|
# Create a serializer with request.DATA
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
|
|
||||||
if serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
# Create PasswordResetForm with the serializer
|
|
||||||
reset_form = self.password_reset_form_class(data=serializer.data)
|
|
||||||
|
|
||||||
if reset_form.is_valid():
|
|
||||||
# Sett some values to trigger the send_email method.
|
|
||||||
opts = {
|
|
||||||
'use_https': request.is_secure(),
|
|
||||||
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
|
|
||||||
'request': request,
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_form.save(**opts)
|
|
||||||
|
|
||||||
# Return the success message with OK HTTP status
|
|
||||||
return Response(
|
|
||||||
{"success": "Password reset e-mail has been sent."},
|
|
||||||
status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response(reset_form._errors,
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response(serializer.errors,
|
return Response(serializer.errors,
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
serializer.save()
|
||||||
|
# Return the success message with OK HTTP status
|
||||||
|
return Response({"success": "Password reset e-mail has been sent."},
|
||||||
|
status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
|
class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
|
@ -283,80 +140,15 @@ class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
Returns the success/fail message.
|
Returns the success/fail message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SetPasswordSerializer
|
serializer_class = PasswordResetConfirmSerializer
|
||||||
|
|
||||||
def post(self, request, uid=None, token=None):
|
def post(self, request):
|
||||||
# Get the UserModel
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
UserModel = get_user_model()
|
if not serializer.is_valid():
|
||||||
|
|
||||||
# Decode the uidb64 to uid to get User object
|
|
||||||
try:
|
|
||||||
uid = uid_decoder(uid)
|
|
||||||
user = UserModel._default_manager.get(pk=uid)
|
|
||||||
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
|
|
||||||
user = None
|
|
||||||
|
|
||||||
# If we get the User object
|
|
||||||
if user:
|
|
||||||
serializer = self.serializer_class(data=request.DATA, user=user)
|
|
||||||
|
|
||||||
if serializer.is_valid():
|
|
||||||
# Construct SetPasswordForm instance
|
|
||||||
form = SetPasswordForm(user=user, data=serializer.data)
|
|
||||||
|
|
||||||
if form.is_valid():
|
|
||||||
if default_token_generator.check_token(user, token):
|
|
||||||
form.save()
|
|
||||||
|
|
||||||
# Return the success message with OK HTTP status
|
|
||||||
return Response(
|
|
||||||
{"success":
|
|
||||||
"Password has been reset with the new password."},
|
|
||||||
status=status.HTTP_200_OK)
|
|
||||||
else:
|
|
||||||
return Response(
|
|
||||||
{"error": "Invalid password reset token."},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
else:
|
|
||||||
return Response(form._errors, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response(serializer.errors,
|
return Response(serializer.errors,
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
serializer.save()
|
||||||
else:
|
return Response({"success": "Password has been reset with the new password."})
|
||||||
return Response({"errors": "Couldn\'t find the user from uid."}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class VerifyEmail(LoggedOutRESTAPIView, GenericAPIView):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Verifies the email of the user through their activation_key.
|
|
||||||
|
|
||||||
Accepts activation_key django argument: key from activation email.
|
|
||||||
Returns the success/fail message.
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = RegistrationProfile
|
|
||||||
|
|
||||||
def get(self, request, activation_key=None):
|
|
||||||
# Get the user registration profile with the activation key
|
|
||||||
target_user = RegistrationProfile.objects.activate_user(activation_key)
|
|
||||||
|
|
||||||
if target_user:
|
|
||||||
# Send the activation signal
|
|
||||||
signals.user_activated.send(sender=ActivationView.__class__,
|
|
||||||
user=target_user,
|
|
||||||
request=request)
|
|
||||||
|
|
||||||
# Return the success message with OK HTTP status
|
|
||||||
ret_msg = "User {0}'s account was successfully activated!".format(
|
|
||||||
target_user.username)
|
|
||||||
return Response({"success": ret_msg}, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ret_msg = "The account was not able to be activated or already activated, please contact support."
|
|
||||||
return Response({"errors": ret_msg}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
||||||
|
@ -368,27 +160,12 @@ class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
||||||
Returns the success/fail message.
|
Returns the success/fail message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SetPasswordSerializer
|
serializer_class = PasswordChangeSerializer
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
# Create a serializer with request.DATA
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
if not serializer.is_valid():
|
||||||
|
|
||||||
if serializer.is_valid():
|
|
||||||
# Construct the SetPasswordForm instance
|
|
||||||
form = SetPasswordForm(user=request.user, data=serializer.data)
|
|
||||||
|
|
||||||
if form.is_valid():
|
|
||||||
form.save()
|
|
||||||
|
|
||||||
# Return the success message with OK HTTP status
|
|
||||||
return Response({"success": "New password has been saved."},
|
|
||||||
status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response(form._errors,
|
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response(serializer.errors,
|
return Response(serializer.errors,
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
serializer.save()
|
||||||
|
return Response({"success": "New password has been saved."})
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -29,7 +29,6 @@ setup(
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'Django>=1.5.0',
|
'Django>=1.5.0',
|
||||||
'django-registration>=1.0',
|
|
||||||
'djangorestframework>=2.3.13',
|
'djangorestframework>=2.3.13',
|
||||||
],
|
],
|
||||||
test_suite='rest_auth.runtests.runtests',
|
test_suite='rest_auth.runtests.runtests',
|
||||||
|
|
2
test_requirements.pip
Normal file
2
test_requirements.pip
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
django-allauth>=0.18.0
|
||||||
|
responses>=0.2.2
|
Loading…
Reference in New Issue
Block a user