Skip to content
Snippets Groups Projects
Commit 81d844bb authored by Chris Li's avatar Chris Li
Browse files

Add basic auth models and views.

parent 9a41bb66
No related branches found
No related tags found
1 merge request!1User Authentication Feature Added
Showing
with 327 additions and 25 deletions
from rest_framework import generics
from rest_framework.response import Response
from knox.models import AuthToken
from core.serializers.login import LoginSerializer
from core.serializers.register import RegisterSerializer
from core.serializers.user import UserSerializer
class RegisterAPI(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
token = AuthToken.objects.create(user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": token[1]
})
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
token = AuthToken.objects.create(user)
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"token": token[1],
})
from rest_framework import status
from rest_framework import generics
from rest_framework.response import Response
from django.contrib.auth.models import User
from core.serializers.password import ChangePasswordSerializer
from rest_framework.permissions import IsAuthenticated
class ChangePasswordView(generics.UpdateAPIView):
"""
An endpoint for changing password.
"""
serializer_class = ChangePasswordSerializer
model = User
permission_classes = (IsAuthenticated,)
def get_object(self, queryset=None):
obj = self.request.user
return obj
def update(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
# Check old password
if not self.object.check_password(serializer.data.get("old_password")):
return Response({
"old_password": ["Wrong password."]
}, status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
response = {
'status': 'success',
'code': status.HTTP_200_OK,
'message': 'Password updated successfully',
'data': []
}
return Response(response)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
......@@ -4,3 +4,6 @@ from django.apps import AppConfig
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'
def ready(self) -> None:
import core.signals
from datetime import datetime, timedelta
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django_countries.fields import CountryField
from django_extensions.db.models import TimeStampedModel
from .utils import UUIDModel
class Profile(TimeStampedModel, UUIDModel):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
birthday = models.DateField(verbose_name="Birthday", null=True)
country = CountryField(null=True, verbose_name="Country")
city = models.CharField(max_length=100, verbose_name="City in English")
affiliation = models.CharField(
max_length=100, verbose_name="Name of your organization in English",
)
photo = models.ImageField(upload_to='media/', max_length=100000, null=True, blank=True, default="https://static.productionready.io/images/smiley-cyrus.jpg")
def __str__(self):
return self.user.username
import uuid
from django.db import models
class UUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
class Meta:
abstract = True
from django.contrib.auth import authenticate
from rest_framework import serializers
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError('Incorrect Credentials Passed.')
from rest_framework import serializers
from django.contrib.auth.models import User
class ChangePasswordSerializer(serializers.Serializer):
model = User
"""
Serializer for password change endpoint.
"""
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
from rest_framework import serializers
from core.models.profile import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('id', 'user', 'bio', 'birthday', 'country', 'city', 'affiliation', 'photo')
from django.contrib.auth.models import User
from rest_framework import serializers
# Register Serializer
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User.objects.create_user(
validated_data['username'],
validated_data['email'],
validated_data['password']
)
return user
from rest_framework import serializers
from django.contrib.auth.models import User
# User Serializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_rest_passwordreset.signals import reset_password_token_created
from django.template.loader import render_to_string
from django.urls import reverse
from core.models.profile import Profile
# @receiver(post_save, sender=User)
# def create_related_profile(sender, instance, created, *args, **kwargs):
# # Notice that we're checking for `created` here. We only want to do this
# # the first time the `User` instance is created. If the save that caused
# # this signal to be run was an update action, we know the user already
# # has a profile.
# if instance and created:
# print(instance,created)
# instance.profile = Profile.objects.create(user=instance)
@receiver(reset_password_token_created)
def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs):
# send an e-mail to the user
context = {
'current_user': reset_password_token.user,
'username': reset_password_token.user.username,
'email': reset_password_token.user.email,
'reset_password_url': "{}?token={}".format(
instance.request.build_absolute_uri(reverse('password_reset:reset-password-confirm')),
reset_password_token.key)
}
# render email text
email_html_message = render_to_string('email/user_reset_password.html', context)
email_plaintext_message = render_to_string('email/user_reset_password.txt', context)
# email_plaintext_message = "{}?token={}".format(reverse('password_reset:reset-password-request'), reset_password_token.key)
msg = EmailMultiAlternatives(
# title:
"Password Reset for {title}".format(title="Some website title"),
# message:
email_plaintext_message,
# from:
"noreply@somehost.local",
# to:
[reset_password_token.user.email]
)
msg.attach_alternative(email_html_message, "text/html")
msg.send()
from django.urls import path
from django.urls import path, include
from knox import views as knox_views
from core.api.auth import RegisterAPI, LoginAPI
from core.api.password import ChangePasswordView
urlpatterns = [
urlpatterns = [
path('api/auth/register', RegisterAPI.as_view(), name='register'),
path('api/auth/login', LoginAPI.as_view(), name='login'),
path('api/auth/logout', knox_views.LogoutView.as_view(), name='logout'),
path('api/change-password', ChangePasswordView.as_view(), name='change-password'),
path('api/password_reset', include('django_rest_passwordreset.urls', namespace='password_reset')),
]
# API
## Authentication
- url: api/auth/register
```json
// request
{
"username": "admin",
"email": "admin@bot.com",
"password": "Password@123"
}
// response
{
"user": {
"id": 2,
"username": "admin1",
"email": "admin1@bot.com"
},
"token": "790e890d571753148bbc9c4447f106e74ecf4d1404f080245f3e259703d58b09"
}
```
- url: api/auth/login
```json
// request
{
"username": "admin",
"password": "Password@123"
}
//response
{
"expiry": "2020-06-29T02:56:44.924698Z",
"token": "99a27b2ebe718a2f0db6224e55b622a59ccdae9cf66861c60979a25ffb4f133e"
}
```
......@@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/4.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
......@@ -31,15 +31,27 @@ ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'core.apps.CoreConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions',
'rest_framework',
'django_rest_passwordreset',
'knox',
'core',
]
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_AUTHENTICATION_CLASSES': [
'knox.auth.TokenAuthentication',
],
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
......@@ -55,7 +67,9 @@ ROOT_URLCONF = 'ece651_backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
......@@ -122,3 +136,10 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Media Settings
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
"""ece651_backend URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
router = routers.DefaultRouter()
urlpatterns = [
path('api/core/', include('core.urls')),
urlpatterns = router.urls
urlpatterns += [
path('', include('core.urls')),
path('admin/', admin.site.urls),
]
urlpatterns += static('media/', document_root=settings.MEDIA_ROOT)
{% autoescape off %}
To initiate the password reset process for you {{ username }} for your BuyforFree Account,
click the link below:
http://josla.com/confirm{{reset_password_url}}
If clicking the link above doesn't work, please copy and paste the URL in a new browser
window instead.
Sincerely,
Josla
{% endautoescape %}
\ No newline at end of file
Josla password reset
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment