Skip to content

Commit

Permalink
added up auth module. (#43)
Browse files Browse the repository at this point in the history
* added up auth module

* init frontend

* added middleware/protected routes
  • Loading branch information
abheektripathy committed Jun 26, 2023
1 parent 1feaac8 commit a4d3a78
Show file tree
Hide file tree
Showing 41 changed files with 11,024 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.log*
*.pyc
.vscode
.vscode
.venv
Empty file.
3 changes: 3 additions & 0 deletions buffalogs/authentication/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions buffalogs/authentication/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AuthenticationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "authentication"
82 changes: 82 additions & 0 deletions buffalogs/authentication/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Generated by Django 4.1.4 on 2023-06-20 18:23

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]

operations = [
migrations.CreateModel(
name="User",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"username",
models.CharField(db_index=True, max_length=255, unique=True),
),
(
"email",
models.EmailField(db_index=True, max_length=255, unique=True),
),
("is_staff", models.BooleanField(default=False)),
("is_verified", models.BooleanField(default=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("avatar", models.CharField(default="", max_length=225)),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
],
options={
"abstract": False,
},
),
]
Empty file.
54 changes: 54 additions & 0 deletions buffalogs/authentication/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
from rest_framework_simplejwt.tokens import RefreshToken


class UserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError("Users should have a username")
if email is None:
raise TypeError("Users should have a Email")

user = self.model(
username=username,
email=self.normalize_email(email),
)
user.set_password(password)
user.save()
return user

def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError("Password should not be none")

user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user


class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255, unique=True, db_index=True)
is_staff = models.BooleanField(default=False)
is_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
avatar = models.CharField(default="", max_length=225)

USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]

objects = UserManager()

def __str__(self):
return self.email

def tokens(self):
refresh = RefreshToken.for_user(self)
return {"refresh": str(refresh), "access": str(refresh.access_token)}


# Create your models here.
96 changes: 96 additions & 0 deletions buffalogs/authentication/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from django.contrib import auth
from django.contrib.auth import logout
from rest_framework import serializers as rfs
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.tokens import TokenError

from .models import User


class RegisterSerializer(rfs.ModelSerializer):
password = rfs.CharField(max_length=68, min_length=6, write_only=True)

default_error_messages = {"username": "The username should only contain alphanumeric characters"}

class Meta:
model = User
fields = ["email", "username", "password"]

def validate(self, attrs):
email = attrs.get("email", "")
username = attrs.get("username", "")

user_filtered_by_email = User.objects.filter(email=email).first()
if user_filtered_by_email:
raise rfs.ValidationError("User with that email already exists")

user_filtered_by_username = User.objects.filter(username=username).first()
if user_filtered_by_username:
raise rfs.ValidationError("User with that username already exists")

if not username.isalnum():
raise rfs.ValidationError(self.default_error_messages)
return attrs

def create(self, validated_data):
return User.objects.create_user(**validated_data)


class LoginSerializer(rfs.ModelSerializer):
email = rfs.EmailField(max_length=255, min_length=3)
password = rfs.CharField(max_length=68, min_length=6, write_only=True)

tokens = rfs.SerializerMethodField()

def get_tokens(self, obj):
user = User.objects.get(email=obj["email"])

return {"refresh": user.tokens()["refresh"], "access": user.tokens()["access"]}

class Meta:
model = User
fields = [
"email",
"password",
"tokens",
]

def validate(self, attrs):
email = attrs.get("email", "")
password = attrs.get("password", "")

user = auth.authenticate(email=email, password=password)

if not user:
raise AuthenticationFailed("Invalid credentials, try again")

return {"email": user.email, "username": user.username, "tokens": user.tokens}


class LogoutSerializer(rfs.Serializer):
default_error_message = {"bad_token": ("Token is expired or invalid")}

def validate(self, attrs):
user = self.context["request"].user
self.tokens = user.tokens()
return attrs

def save(self):
try:
logout(self.context["request"])
except TokenError:
self.fail("bad_token")


class UserSerializer(rfs.ModelSerializer):
class Meta:
model = User
fields = (
"id",
"email",
"username",
"created_at",
"updated_at",
"avatar",
"is_staff",
)
3 changes: 3 additions & 0 deletions buffalogs/authentication/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
11 changes: 11 additions & 0 deletions buffalogs/authentication/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import path
from rest_framework_simplejwt.views import TokenRefreshView

from .views import LoginAPIView, LogoutAPIView, RegisterView

urlpatterns = [
path("login", LoginAPIView.as_view(), name="login"),
path("register", RegisterView.as_view(), name="register"),
path("logout", LogoutAPIView.as_view(), name="logout"),
path("token/refresh", TokenRefreshView.as_view(), name="token_refresh"),
]
80 changes: 80 additions & 0 deletions buffalogs/authentication/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import logging
import os

from django.contrib.auth import get_user_model
from django.http import HttpResponsePermanentRedirect
from django.shortcuts import render
from rest_framework import generics, permissions, status
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response

from .serializers import LoginSerializer, LogoutSerializer, RegisterSerializer, UserSerializer

logger = logging.getLogger(__name__)


User = get_user_model()


class CustomRedirect(HttpResponsePermanentRedirect):
allowed_schemes = [os.environ.get("APP_SCHEME"), "http", "https"]


class RegisterView(generics.GenericAPIView):

serializer_class = RegisterSerializer
authentication_classes = []
permission_classes = []

def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user = User.objects.get(email=user_data["email"])

return Response(
{
"status": "successful",
},
status=status.HTTP_201_CREATED,
)


class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
authentication_classes = []
permission_classes = []

def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.data
data["username"] = serializer.validated_data["username"]
return Response(data, status=status.HTTP_200_OK)


class LogoutAPIView(generics.GenericAPIView):
serializer_class = LogoutSerializer
permission_classes = (permissions.IsAuthenticated,)

def post(self, request):

serializer = self.serializer_class(data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)
serializer.save()

return Response({"status": "successful"}, status=status.HTTP_200_OK)


class MeAPIView(generics.ListAPIView):
serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticated,)

def get_queryset(self):
user = self.request.user
return User.objects.filter(id=user.id)


# Create your views here.
Loading

0 comments on commit a4d3a78

Please sign in to comment.