Skip to content

Commit

Permalink
Completed authentication view and test
Browse files Browse the repository at this point in the history
  • Loading branch information
riadelimemmedov committed Mar 25, 2024
1 parent e9e5bdc commit 88992b1
Show file tree
Hide file tree
Showing 16 changed files with 726 additions and 271 deletions.
4 changes: 2 additions & 2 deletions backend/apps/transaction/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.contrib import admin

from .models import Transaction
from .validate_field import validate_confirmations, validate_from_user
from .validate_field import validate_confirmations, validate_wallet_address

# Register your models here.

Expand All @@ -21,7 +21,7 @@ def clean(self):
return
if not validate_confirmations(confirmations):
self.add_error("confirmations", "Confirmations value 1 or 0")
if not validate_from_user(from_user):
if not validate_wallet_address(from_user):
self.add_error("from_user", "Please input valid wallet address")


Expand Down
6 changes: 3 additions & 3 deletions backend/apps/transaction/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
from rest_framework import serializers

from .models import Transaction
from .validate_field import validate_confirmations, validate_from_user
from .validate_field import validate_confirmations, validate_wallet_address


# !TransactionSerializer
class TransactionSerializer(serializers.ModelSerializer):
from_user = serializers.CharField(validators=[validate_from_user])
from_user = serializers.CharField(validators=[validate_wallet_address])
confirmations = serializers.CharField(validators=[validate_confirmations])

def validate(self, attrs):
from_user = attrs.get("from_user")
confirmations = attrs.get("confirmations")

if not validate_from_user(from_user):
if not validate_wallet_address(from_user):
raise serializers.ValidationError("Please input valid wallet address")
if not validate_confirmations(confirmations):
raise serializers.ValidationError("Confirmations value 1 or 0")
Expand Down
4 changes: 2 additions & 2 deletions backend/apps/transaction/validate_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def validate_confirmations(value):
return value


# ?validate_from_user
def validate_from_user(value):
# ?validate_wallet_address
def validate_wallet_address(value):
pattern = re.compile("^0x[a-fA-F0-9]{40}$")
return bool(pattern.match(f"{value}"))
17 changes: 10 additions & 7 deletions backend/apps/user_profile/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib.auth import get_user_model
from django.core.validators import FileExtensionValidator
from django.db import models
from django.db.models.signals import post_save
Expand All @@ -12,6 +13,8 @@

# Create your models here.

User = get_user_model()


# *ActiveQueryset
class ActiveQueryset(models.QuerySet):
Expand Down Expand Up @@ -98,15 +101,15 @@ class Meta:

def save(self, *args, **kwargs):
if not self.full_name:
full_name = setFullName(
self.first_name if self.first_name is not None else "",
self.last_name if self.last_name is not None else "",
)
is_exists = __class__.objects.filter(full_name=full_name).exists()
# full_name = setFullName(
# self.first_name if self.first_name is not None else "",
# self.last_name if self.last_name is not None else "",
# )
is_exists = User.objects.filter(username=self.user.username).exists()
if is_exists:
self.full_name = f"{full_name}_{self.profile_key}"
self.full_name = f"{self.user.username}_{self.profile_key}"
else:
self.full_name = f"{full_name}"
self.full_name = f"{self.user.username}"
self.slug = slugify(self.full_name)
super(Profile, self).save(*args, **kwargs)

Expand Down
7 changes: 5 additions & 2 deletions backend/apps/users/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display_links = ["id", "email", "wallet_address"]
list_display_links = ["id", "email", "wallet_address", "username"]
list_display = (
"id",
"email",
"wallet_address",
"is_staff",
"is_active",
"username",
)
list_filter = (
"email",
Expand All @@ -36,7 +37,7 @@ class CustomUserAdmin(UserAdmin):
"is_active",
)
fieldsets = (
(None, {"fields": ("email", "wallet_address", "password")}),
(None, {"fields": ("email", "wallet_address", "password", "username")}),
(
"Permissions",
{"fields": ("is_staff", "is_active", "groups", "user_permissions")},
Expand All @@ -55,6 +56,8 @@ class CustomUserAdmin(UserAdmin):
"is_active",
"groups",
"user_permissions",
"wallet_address",
"username",
),
},
),
Expand Down
11 changes: 11 additions & 0 deletions backend/apps/users/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.contrib.auth import get_user_model

# User
User = get_user_model()


# ?is_exists_wallet_address
def is_exists_wallet_address(wallet_address: str):
if User.objects.filter(wallet_address=wallet_address).exists():
return True
return False
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 5.0.1 on 2024-03-25 18:34

from django.db import migrations, models

import config.helpers


class Migration(migrations.Migration):

dependencies = [
("users", "0007_initial"),
]

operations = [
migrations.AlterField(
model_name="customuser",
name="wallet_address",
field=models.CharField(
db_index=True,
max_length=100,
null=True,
unique=True,
validators=[config.helpers.is_valid_wallet_address],
verbose_name="Wallet address",
),
),
]
2 changes: 1 addition & 1 deletion backend/apps/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
unique=True,
db_index=True,
max_length=100,
blank=True,
blank=False,
null=True,
validators=[is_valid_wallet_address],
)
Expand Down
61 changes: 61 additions & 0 deletions backend/apps/users/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from django.contrib.auth import authenticate
from rest_framework import serializers

from apps.transaction.validate_field import (
validate_confirmations,
validate_wallet_address,
)

from .helpers import is_exists_wallet_address
from .models import CustomUser


# !CustomUserSerializer
class CustomUserSerializer(serializers.ModelSerializer):
"""
Serializer class to serialize CustomUser model.
"""

class Meta:
model = CustomUser
fields = ("id", "username", "email", "wallet_address")


# !UserRegisterationSerializer
class UserRegisterationSerializer(serializers.ModelSerializer):
"""
Serializer class to serialize registration requests and create a new user.
"""

wallet_address = serializers.CharField(validators=[validate_wallet_address])

def validate_wallet_address(self, wallet_address):
if not validate_wallet_address(wallet_address):
raise serializers.ValidationError("Please input valid wallet address")
elif is_exists_wallet_address(wallet_address):
raise serializers.ValidationError("This wallet address already set other")
return wallet_address

class Meta:
model = CustomUser
fields = ("id", "username", "email", "password", "wallet_address")
extra_kwargs = {"password": {"write_only": True}}

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


# !UserLoginSerializer
class UserLoginSerializer(serializers.Serializer):
"""
Serializer class to authenticate users with email and password.
"""

email = serializers.CharField()
password = serializers.CharField(write_only=True)

def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Incorrect Credentials")
17 changes: 16 additions & 1 deletion backend/apps/users/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
from django.urls import path
from rest_framework_simplejwt.views import TokenRefreshView

from .views import (
UserAPIView,
UserLoginAPIView,
UserLogoutAPIView,
UserRegisterationAPIView,
)

# from .views import hello_world


app_name = "users"
# urlpatterns = [path("sayhello/", hello_world, name="hello_world")]
urlpatterns = [
path("register/", UserRegisterationAPIView.as_view(), name="create-user"),
path("login/", UserLoginAPIView.as_view(), name="login-user"),
path("token/refresh/", TokenRefreshView.as_view(), name="token-refresh"),
path("logout/", UserLogoutAPIView.as_view(), name="logout-user"),
path("", UserAPIView.as_view(), name="user-info"),
]
91 changes: 83 additions & 8 deletions backend/apps/users/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,87 @@
from django.shortcuts import render
from rest_framework.decorators import api_view
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.generics import GenericAPIView, RetrieveUpdateAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken

# Create your views here.
from apps.user_profile.models import Profile

from .serializers import (
CustomUserSerializer,
UserLoginSerializer,
UserRegisterationSerializer,
)

# @api_view(["GET", "POST"])
# def hello_world(request):
# if request.method == "POST":
# return Response({"message": "Got some data!", "data": request.data})
# return {"message": "Hello, world!"}
# User
User = get_user_model()


# !UserRegisterationAPIView
class UserRegisterationAPIView(GenericAPIView):
"""
An endpoint for the client to create a new User.
"""

permission_classes = (AllowAny,)
serializer_class = UserRegisterationSerializer

def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
token = RefreshToken.for_user(user)
data = serializer.data
data["tokens"] = {"refresh": str(token), "access": str(token.access_token)}
return Response(data, status=status.HTTP_201_CREATED)


# !UserLoginAPIView
class UserLoginAPIView(GenericAPIView):
"""
An endpoint to authenticate existing users using their email and password.
"""

permission_classes = (AllowAny,)
serializer_class = UserLoginSerializer

def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
serializer = CustomUserSerializer(user)
token = RefreshToken.for_user(user)
data = serializer.data
data["tokens"] = {"refresh": str(token), "access": str(token.access_token)}
return Response(data, status=status.HTTP_200_OK)


# !UserLogoutAPIView
class UserLogoutAPIView(GenericAPIView):
"""
An endpoint to logout users.
"""

permission_classes = (IsAuthenticated,)

def post(self, request, *args, **kwargs):
try:
refresh_token = request.data["refresh"]
token = RefreshToken(refresh_token)
token.blacklist()
return Response(status=status.HTTP_205_RESET_CONTENT)
except Exception:
return Response(status=status.HTTP_400_BAD_REQUEST)


# !UserAPIView
class UserAPIView(RetrieveUpdateAPIView):
"""
Get, Update user information
"""

permission_classes = (IsAuthenticated,)
serializer_class = CustomUserSerializer

def get_object(self):
return self.request.user
5 changes: 3 additions & 2 deletions backend/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@
# ],
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
}

Expand Down Expand Up @@ -359,8 +360,8 @@

# !SIMPLE_JWT
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
"REFRESH_TOKEN_LIFETIME": timedelta(days=14),
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=180),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"UPDATE_LAST_LOGIN": False,
Expand Down
4 changes: 2 additions & 2 deletions backend/config/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def setPetName(name: str) -> str:
def is_valid_wallet_address(wallet_address: str):
pattern = r"^0x[a-fA-F0-9]{40}$"
if wallet_address == "" or wallet_address is None:
return ""
return ValidationError("Please enter a wallet address")
elif not re.match(pattern, wallet_address):
raise ValidationError("Invalid MetaMask address.")
raise ValidationError("Invalid MetaMask wallet address address.")
return True


Expand Down
Loading

0 comments on commit 88992b1

Please sign in to comment.