mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-06-29 17:13:03 +03:00
Merge b3d07028ca
into cdd04aa9be
This commit is contained in:
commit
c89ca76f62
8
demo/Dockerfile
Normal file
8
demo/Dockerfile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
FROM python:3
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
RUN mkdir /code
|
||||||
|
WORKDIR /code
|
||||||
|
COPY requirements.txt /code/
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
COPY . /code/
|
||||||
|
|
|
@ -31,7 +31,7 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
# 'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
|
|
||||||
|
@ -39,12 +39,15 @@ INSTALLED_APPS = (
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'rest_auth',
|
'rest_auth',
|
||||||
|
|
||||||
|
|
||||||
'allauth',
|
'allauth',
|
||||||
'allauth.account',
|
'allauth.account',
|
||||||
'rest_auth.registration',
|
'rest_auth.registration',
|
||||||
'allauth.socialaccount',
|
'allauth.socialaccount',
|
||||||
'allauth.socialaccount.providers.facebook',
|
'allauth.socialaccount.providers.facebook',
|
||||||
'rest_framework_swagger',
|
'rest_framework_swagger',
|
||||||
|
|
||||||
|
'axes',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE = (
|
||||||
|
@ -54,10 +57,9 @@ MIDDLEWARE = (
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
)
|
|
||||||
|
|
||||||
# For backwards compatibility for Django 1.8
|
'axes.middleware.AxesMiddleware',
|
||||||
MIDDLEWARE_CLASSES = MIDDLEWARE
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'demo.urls'
|
ROOT_URLCONF = 'demo.urls'
|
||||||
|
|
||||||
|
@ -127,3 +129,27 @@ SWAGGER_SETTINGS = {
|
||||||
'LOGIN_URL': 'login',
|
'LOGIN_URL': 'login',
|
||||||
'LOGOUT_URL': 'logout',
|
'LOGOUT_URL': 'logout',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
|
||||||
|
# AxesBackend should be the first backend in the AUTHENTICATION_BACKENDS list.
|
||||||
|
'axes.backends.AxesBackend',
|
||||||
|
|
||||||
|
# Required for rest-auth when using Axes to prevent 'Unable to log in with provided credentials.'
|
||||||
|
'allauth.account.auth_backends.AuthenticationBackend',
|
||||||
|
|
||||||
|
# Django ModelBackend is the default authentication backend.
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
]
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
|
||||||
|
'LOCATION': 'django_database_cache',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REST_AUTH_SERIALIZERS = {
|
||||||
|
'LOGIN_SERIALIZER': 'myapp.serializers.RestAuthAxesLoginSerializer',
|
||||||
|
# 'TOKEN_SERIALIZER': 'path.to.custom.TokenSerializer',
|
||||||
|
}
|
||||||
|
|
13
demo/docker-compose.yml
Normal file
13
demo/docker-compose.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
image: gableroux/django-rest-auth-demo
|
||||||
|
command: python manage.py runserver 0.0.0.0:1234
|
||||||
|
environment:
|
||||||
|
PYTHONUNBUFFERED: 1
|
||||||
|
volumes:
|
||||||
|
- .:/code
|
||||||
|
ports:
|
||||||
|
- "1234:1234"
|
0
demo/myapp/__init__.py
Normal file
0
demo/myapp/__init__.py
Normal file
3
demo/myapp/admin.py
Normal file
3
demo/myapp/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
5
demo/myapp/apps.py
Normal file
5
demo/myapp/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MyappConfig(AppConfig):
|
||||||
|
name = 'myapp'
|
0
demo/myapp/migrations/__init__.py
Normal file
0
demo/myapp/migrations/__init__.py
Normal file
3
demo/myapp/models.py
Normal file
3
demo/myapp/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
16
demo/myapp/serializers.py
Normal file
16
demo/myapp/serializers.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from axes.helpers import get_lockout_message
|
||||||
|
from rest_auth import serializers
|
||||||
|
from rest_auth.serializers import LoginSerializer
|
||||||
|
from rest_framework import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyAbstractClass
|
||||||
|
class RestAuthAxesLoginSerializer(LoginSerializer):
|
||||||
|
|
||||||
|
def validate(self, attrs) -> dict:
|
||||||
|
try:
|
||||||
|
return super().validate(attrs)
|
||||||
|
except exceptions.ValidationError as e:
|
||||||
|
if getattr(self.context['request'], 'axes_locked_out', None):
|
||||||
|
raise serializers.ValidationError(get_lockout_message())
|
||||||
|
raise e
|
71
demo/myapp/tests/test_serializers.py
Normal file
71
demo/myapp/tests/test_serializers.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.test import TestCase
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
from myapp import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class TestRestAuthAxesLoginSerializer(TestCase):
|
||||||
|
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.request = HttpRequest()
|
||||||
|
|
||||||
|
def test_validate_wrong_user(self) -> None:
|
||||||
|
serializer = serializers.RestAuthAxesLoginSerializer(
|
||||||
|
context=dict(request=self.request)
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(ValidationError, 'Unable to log in with provided credentials.'):
|
||||||
|
serializer.validate({
|
||||||
|
'username': 'test',
|
||||||
|
'email': 'test@example.com',
|
||||||
|
'password': 'test'
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_validate_good_user(self) -> None:
|
||||||
|
User.objects.create_user(
|
||||||
|
username='test',
|
||||||
|
email='test@example.com',
|
||||||
|
password='test'
|
||||||
|
)
|
||||||
|
serializer = serializers.RestAuthAxesLoginSerializer(
|
||||||
|
context=dict(request=self.request)
|
||||||
|
)
|
||||||
|
attrs = serializer.validate({
|
||||||
|
'username': 'test',
|
||||||
|
'email': 'test@example.com',
|
||||||
|
'password': 'test'
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertIsNotNone(attrs)
|
||||||
|
|
||||||
|
def test_validate_axes_locked_out(self) -> None:
|
||||||
|
good_password_creds = {
|
||||||
|
'username': 'test',
|
||||||
|
'email': 'test@example.com',
|
||||||
|
'password': 'good_password'
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_password_creds = {
|
||||||
|
'username': 'test',
|
||||||
|
'email': 'test@example.com',
|
||||||
|
'password': 'bad_password'
|
||||||
|
}
|
||||||
|
|
||||||
|
User.objects.create_user(**good_password_creds)
|
||||||
|
serializer = serializers.RestAuthAxesLoginSerializer(
|
||||||
|
context=dict(request=self.request)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(settings.AXES_FAILURE_LIMIT - 1):
|
||||||
|
with self.assertRaisesMessage(ValidationError, 'Unable to log in with provided credentials.'):
|
||||||
|
serializer.validate(bad_password_creds)
|
||||||
|
|
||||||
|
account_locked_message = 'Account locked: too many login attempts. Contact an admin to unlock your account.'
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(ValidationError, account_locked_message):
|
||||||
|
serializer.validate(bad_password_creds)
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(ValidationError, account_locked_message):
|
||||||
|
serializer.validate(good_password_creds)
|
3
demo/myapp/views.py
Normal file
3
demo/myapp/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
|
@ -1,6 +0,0 @@
|
||||||
django>=1.9.0
|
|
||||||
django-rest-auth==0.9.5
|
|
||||||
djangorestframework>=3.7.0
|
|
||||||
django-allauth>=0.24.1
|
|
||||||
six==1.9.0
|
|
||||||
django-rest-swagger==2.0.7
|
|
7
demo/requirements.txt
Normal file
7
demo/requirements.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
django==1.11.22
|
||||||
|
django-rest-auth==0.9.5
|
||||||
|
djangorestframework>=3.9.4
|
||||||
|
django-allauth>=0.39.1
|
||||||
|
six==1.12.0
|
||||||
|
django-rest-swagger==2.2.0
|
||||||
|
django-axes==5.0.7
|
|
@ -10,7 +10,7 @@ Do these steps to make it running (ideally in virtualenv).
|
||||||
cd /tmp
|
cd /tmp
|
||||||
git clone https://github.com/Tivix/django-rest-auth.git
|
git clone https://github.com/Tivix/django-rest-auth.git
|
||||||
cd django-rest-auth/demo/
|
cd django-rest-auth/demo/
|
||||||
pip install -r requirements.pip
|
pip install -r requirements.txt
|
||||||
python manage.py migrate --settings=demo.settings --noinput
|
python manage.py migrate --settings=demo.settings --noinput
|
||||||
python manage.py runserver --settings=demo.settings
|
python manage.py runserver --settings=demo.settings
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user