This commit is contained in:
Gabriel Le Breton 2022-04-03 16:06:32 +09:00 committed by GitHub
commit c89ca76f62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 160 additions and 11 deletions

8
demo/Dockerfile Normal file
View 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/

View File

@ -31,7 +31,7 @@ INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
# 'django.contrib.messages',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
@ -39,12 +39,15 @@ INSTALLED_APPS = (
'rest_framework.authtoken',
'rest_auth',
'allauth',
'allauth.account',
'rest_auth.registration',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'rest_framework_swagger',
'axes',
)
MIDDLEWARE = (
@ -54,10 +57,9 @@ MIDDLEWARE = (
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
# For backwards compatibility for Django 1.8
MIDDLEWARE_CLASSES = MIDDLEWARE
'axes.middleware.AxesMiddleware',
)
ROOT_URLCONF = 'demo.urls'
@ -127,3 +129,27 @@ SWAGGER_SETTINGS = {
'LOGIN_URL': 'login',
'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
View 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
View File

3
demo/myapp/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
demo/myapp/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class MyappConfig(AppConfig):
name = 'myapp'

View File

3
demo/myapp/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

16
demo/myapp/serializers.py Normal file
View 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

View 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
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@ -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
View 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

View File

@ -10,7 +10,7 @@ Do these steps to make it running (ideally in virtualenv).
cd /tmp
git clone https://github.com/Tivix/django-rest-auth.git
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 runserver --settings=demo.settings