· 4 years ago · Jan 26, 2021, 01:36 PM
1# urls.py
2
3from django.contrib import admin
4from django.urls import include, path
5
6from dj_rest_auth.registration.views import VerifyEmailView, ConfirmEmailView
7from dj_rest_auth.views import PasswordResetConfirmView
8
9urlpatterns = [
10 path('admin/', admin.site.urls),
11 path('dj-rest-auth/', include('dj_rest_auth.urls'), name='dj-rest-auth'),
12 path(
13 'dj-rest-auth/registration/account-confirm-email/<str:key>/',
14 ConfirmEmailView.as_view(),
15 ),
16 path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')),
17 path('dj-rest-auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'),
18 path(
19 'rest-auth/password/reset/confirm/<slug:uidb64>/<slug:token>/',
20 PasswordResetConfirmView.as_view(),
21 name='password_reset_confirm',
22 ),
23]
24================================================================================================================
25
26# settings.py
27
28from datetime import timedelta
29from functools import partial
30from pathlib import Path
31
32import dj_database_url
33from decouple import Csv, config
34
35# Build paths inside the project like this: BASE_DIR / 'subdir'.
36BASE_DIR = Path(__file__).resolve().parent.parent
37
38
39# Quick-start development settings - unsuitable for production
40# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
41
42# SECURITY WARNING: keep the secret key used in production secret!
43SECRET_KEY = config('SECRET_KEY')
44
45# SECURITY WARNING: don't run with debug turned on in production!
46DEBUG = config('DEBUG', cast=bool)
47
48# Allowed Hosts
49ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
50
51# Tells the framework which will be the default class used as a user
52AUTH_USER_MODEL = 'accounts.User'
53
54# Application definition
55
56INSTALLED_APPS = [
57 # Django Apps
58 'django.contrib.admin',
59 'django.contrib.auth',
60 'django.contrib.contenttypes',
61 'django.contrib.sessions',
62 'django.contrib.messages',
63 'django.contrib.staticfiles',
64 # Local Apps
65 'billapp.accounts',
66 # Third-Party Apps
67 'rest_framework',
68 'rest_framework.authtoken',
69 'corsheaders',
70 'dj_rest_auth',
71 'django.contrib.sites',
72 'allauth',
73 'allauth.account',
74 'allauth.socialaccount',
75 'dj_rest_auth.registration',
76]
77
78AUTHENTICATION_BACKENDS = [
79 # Needed to login by email in Django admin, regardless of allauth
80 'django.contrib.auth.backends.ModelBackend',
81 # allauth specific authentication methods, such as login by e-mail
82 'allauth.account.auth_backends.AuthenticationBackend',
83]
84
85SITE_ID = 1
86
87ACCOUNT_AUTHENTICATION_METHOD = 'email'
88ACCOUNT_EMAIL_REQUIRED = True
89ACCOUNT_UNIQUE_EMAIL = True
90ACCOUNT_USER_MODEL_USERNAME_FIELD = None
91ACCOUNT_USERNAME_REQUIRED = False
92# ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
93ACCOUNT_CONFIRM_EMAIL_ON_GET = True
94LOGIN_URL = 'http://localhost:8000/dj-rest-auth/login'
95
96REST_USE_JWT = True
97JWT_AUTH_COOKIE = 'billapp'
98
99REST_AUTH_REGISTER_SERIALIZERS = {
100 'REGISTER_SERIALIZER': 'billapp.accounts.serializers.CustomRegisterSerializer',
101}
102
103REST_AUTH_SERIALIZERS = {
104 'USER_DETAILS_SERIALIZER': 'billapp.accounts.serializers.CustomRegisterSerializer',
105}
106
107EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
108# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
109# EMAIL_HOST = 'your.email.host'
110# EMAIL_USE_TLS = True
111# EMAIL_PORT = 587
112# EMAIL_HOST_USER = 'your email host user'
113# EMAIL_HOST_PASSWORD = 'your email host password'
114
115MIDDLEWARE = [
116 'corsheaders.middleware.CorsMiddleware',
117 'django.middleware.security.SecurityMiddleware',
118 'django.contrib.sessions.middleware.SessionMiddleware',
119 'django.middleware.common.CommonMiddleware',
120 'django.middleware.csrf.CsrfViewMiddleware',
121 'django.contrib.auth.middleware.AuthenticationMiddleware',
122 'django.contrib.messages.middleware.MessageMiddleware',
123 'django.middleware.clickjacking.XFrameOptionsMiddleware',
124]
125
126# CONFIGURATION CORS
127CORS_ORIGIN_ALLOW_ALL = True
128CORS_ALLOW_CREDENTIALS = True
129# CORS_ORIGIN_WHITELIST = [
130# 'https://127.0.0.1:8080',
131# 'https://127.0.0.1:8000',
132# ]
133
134# CONFIGURATION REST_FRAMEWORK
135REST_FRAMEWORK = {
136 'TEST_REQUEST_RENDERER_CLASSES': [
137 'rest_framework.renderers.MultiPartRenderer',
138 'rest_framework.renderers.JSONRenderer',
139 'rest_framework.renderers.TemplateHTMLRenderer',
140 ],
141 'DEFAULT_AUTHENTICATION_CLASSES': [
142 'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
143 # 'rest_framework_simplejwt.authentication.JWTAuthentication',
144 ],
145 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
146 'PAGE_SIZE': 20,
147}
148
149
150# CONFIGURATION SIMPLE JWT
151SIMPLE_JWT = {
152 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
153 'REFRESH_TOKEN_LIFETIME': timedelta(minutes=60),
154 'ROTATE_REFRESH_TOKENS': False,
155 'BLACKLIST_AFTER_ROTATION': True,
156 'UPDATE_LAST_LOGIN': False,
157 'ALGORITHM': 'HS256',
158 'VERIFYING_KEY': None,
159 'AUDIENCE': None,
160 'ISSUER': None,
161 'AUTH_HEADER_TYPES': ('Bearer',),
162 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
163 'USER_ID_FIELD': 'id',
164 'USER_ID_CLAIM': 'user_id',
165 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
166 'TOKEN_TYPE_CLAIM': 'token_type',
167 'JTI_CLAIM': 'jti',
168 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
169 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
170 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
171}
172
173ROOT_URLCONF = 'billapp.urls'
174
175TEMPLATES = [
176 {
177 'BACKEND': 'django.template.backends.django.DjangoTemplates',
178 'DIRS': [],
179 'APP_DIRS': True,
180 'OPTIONS': {
181 'context_processors': [
182 'django.template.context_processors.debug',
183 'django.template.context_processors.request',
184 'django.contrib.auth.context_processors.auth',
185 'django.contrib.messages.context_processors.messages',
186 ],
187 },
188 },
189]
190
191WSGI_APPLICATION = 'billapp.wsgi.application'
192
193
194# Database
195# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
196
197default_db_url = default_db_url = 'sqlite:///' + str(BASE_DIR / 'db.sqlite3')
198
199parse_database = partial(dj_database_url.parse, conn_max_age=600)
200
201DATABASES = {'default': config('DATABASE_URL', default=default_db_url, cast=parse_database)}
202
203# Password validation
204# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
205
206AUTH_PASSWORD_VALIDATORS = [
207 {
208 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
209 },
210 {
211 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
212 },
213 {
214 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
215 },
216 {
217 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
218 },
219]
220
221
222# Internationalization
223# https://docs.djangoproject.com/en/3.1/topics/i18n/
224
225LANGUAGE_CODE = 'en-us'
226
227TIME_ZONE = 'UTC'
228
229USE_I18N = True
230
231USE_L10N = True
232
233USE_TZ = True
234
235
236# Static files (CSS, JavaScript, Images)
237# https://docs.djangoproject.com/en/3.1/howto/static-files/
238
239STATIC_URL = '/static/'
240================================================================================================================
241
242# serializers.py
243
244from django.db import transaction
245from rest_framework import serializers
246from dj_rest_auth.registration.serializers import RegisterSerializer
247
248
249class CustomRegisterSerializer(RegisterSerializer):
250 first_name = serializers.CharField(max_length=30)
251 last_name = serializers.CharField(max_length=150)
252 email = serializers.EmailField()
253 cpf = serializers.CharField(max_length=11)
254
255 # Define transaction.atomic to rollback the save operation in case of error
256 @transaction.atomic
257 def save(self, request):
258 user = super().save(request)
259 user.first_name = self.data.get('first_name')
260 user.last_name = self.data.get('last_name')
261 user.email = self.data.get('email')
262 user.cpf = self.data.get('cpf')
263 user.save()
264 return user
265
266================================================================================================================
267
268# conftest.py
269
270import pytest
271from django.contrib.auth import get_user_model
272from rest_framework.test import APIClient
273from rest_framework_simplejwt.tokens import RefreshToken
274
275
276@pytest.fixture
277def create_user(db):
278 # This fixture is responsable for create user.
279 first_name = 'Foo'
280 last_name = 'Bar'
281 email = 'foo@email.com'
282 cpf = '17786146806'
283 password = 'bar'
284 user = get_user_model().objects.create_user(
285 first_name=first_name, last_name=last_name, email=email, password=password, cpf=cpf
286 )
287 return user
288
289@pytest.fixture
290def api_client(create_user):
291 # This fixture is responsable for authorization
292 client = APIClient()
293 refresh = RefreshToken.for_user(create_user)
294 client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
295 return client
296
297
298# app/tests/test_user.py
299
300from django.urls import reverse
301from rest_framework import status
302
303from conftest import get_user_model, pytest
304
305
306def test_count_user(create_user):
307 assert get_user_model().objects.count() == 1
308
309
310def test_get_first_name(create_user):
311 assert get_user_model().objects.first().first_name == 'Foo'
312
313
314def test_get_last_name(create_user):
315 assert get_user_model().objects.first().last_name == 'Bar'
316
317
318def test_get_email(create_user):
319 assert get_user_model().objects.first().email == 'foo@email.com'
320
321
322def test_get_cpf(create_user):
323 assert get_user_model().objects.first().cpf == '17786146806'
324
325Obs.: Esses 5 primeiros passam (success)
326================================================================================================================
327
328Obs.: Daqui pra baixo falha:
329
330def test_user_create(api_client):
331 resp = api_client.post(
332 '/dj-rest-auth/registration/',
333 data={
334 'first_name': 'Fulano',
335 'last_name': 'de Tal',
336 'email': 'fulano@email.com',
337 'cpf': '17786146806',
338 'password1': 'teste@123',
339 'password2': 'teste@123',
340 },
341 format='json',
342 )
343 assert resp.status_code == status.HTTP_201_CREATED
344
345
346@pytest.mark.django_db
347@pytest.mark.parametrize(
348 'email, password, status_code',
349 [
350 ('', '', status.HTTP_400_BAD_REQUEST),
351 ('', 'bar', status.HTTP_400_BAD_REQUEST),
352 ('foo@email.com', '', status.HTTP_400_BAD_REQUEST),
353 ('foo@email.com', 1, status.HTTP_401_UNAUTHORIZED),
354 ('foo@email.com', 'bar', status.HTTP_200_OK),
355 ],
356)
357def test_login_data_validation(email, password, status_code, api_client):
358 url = reverse('dj-rest-auth:login')
359 data = {'email': email, 'password': password}
360 response = api_client.post(url, data=data)
361 assert response.status_code == status_code
362
363================================================================================================================
364Erro:
365
366> raise NoReverseMatch("%s is not a registered namespace" % key)
367E django.urls.exceptions.NoReverseMatch: 'dj-rest-auth' is not a registered namespace
368
369.venv/lib/python3.9/site-packages/django/urls/base.py:83: NoReverseMatch
370============================================ short test summary info =============================================
371FAILED billapp/accounts/tests/test_user.py::test_user_create - django.db.utils.IntegrityError: UNIQUE constrain...
372FAILED billapp/accounts/tests/test_user.py::test_login_data_validation[--400] - django.urls.exceptions.NoRevers...
373FAILED billapp/accounts/tests/test_user.py::test_login_data_validation[-bar-400] - django.urls.exceptions.NoRev...
374FAILED billapp/accounts/tests/test_user.py::test_login_data_validation[foo@email.com--400] - django.urls.except...
375FAILED billapp/accounts/tests/test_user.py::test_login_data_validation[foo@email.com-1-401] - django.urls.excep...
376FAILED billapp/accounts/tests/test_user.py::test_login_data_validation[foo@email.com-bar-200] - django.urls.exc...
377========================================== 6 failed, 5 passed in 8.57s ===========================================
378