Skip to content

Commit

Permalink
changed email field to onetoonefield and created a confiremail model,…
Browse files Browse the repository at this point in the history
… updated register_user to send verification before user-registration, handled some error handlings in views.py for user-registratio and updated serializers.py
  • Loading branch information
abbastoof committed Aug 17, 2024
1 parent a44eba3 commit b5fb47d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 55 deletions.
7 changes: 7 additions & 0 deletions Backend/user_service/user_service/user_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ def user_directory_path(instance, filename):
filename = f'{uuid.uuid4()}.{ext}'
return os.path.join(str(instance.id), filename)

class ConfirmEmail(models.Model):
user_email = models.EmailField(unique=True, primary_key=True)
verify_status = models.BooleanField(default=False)
otp = models.CharField(null=True, blank=True)
otp_expiry_time = models.DateTimeField(blank=True, null=True)

class UserProfileModel(AbstractUser):
"""
User class to define the user model.
Expand All @@ -22,6 +28,7 @@ class UserProfileModel(AbstractUser):
Email: The email field is required for the user model.
"""
email = models.OneToOneField(ConfirmEmail, related_name='user_profile', on_delete=models.CASCADE)
avatar = models.ImageField(upload_to=user_directory_path, null=True, blank=True, default='default.jpg')
friends = models.ManyToManyField("self", blank=True, symmetrical=True)
online_status = models.BooleanField(default=False)
Expand Down
26 changes: 14 additions & 12 deletions Backend/user_service/user_service/user_app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.core.exceptions import ValidationError
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import UserProfileModel, FriendRequest, GameRoom
from .models import UserProfileModel, FriendRequest, GameRoom, ConfirmEmail
from .validators import CustomPasswordValidator

class GameRoomSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -57,9 +57,6 @@ class UserSerializer(serializers.ModelSerializer):
create: Method to create a new user.
update: Method to update a user.
"""
email = serializers.EmailField(
validators=[UniqueValidator(queryset=UserProfileModel.objects.all())]
)
avatar = serializers.ImageField(required=False)
friends = serializers.PrimaryKeyRelatedField(
many=True, queryset=UserProfileModel.objects.all(), required=False # required=False means that the field is not required
Expand All @@ -83,31 +80,31 @@ class Meta:

### Password should be strong password, minimum 8 characters, at least one uppercase letter, one lowercase letter, one number and one special character

def create(self, validate_data) -> UserProfileModel:
def create(self, validated_data) -> UserProfileModel:
"""
Method to create a new user.
This method creates a new user with the given data.
The password is validated using CustomPasswordValidator.
The password is hashed before saving the user object.
Args:
validate_data: The data to validate.
validated_data: The data to validate.
Returns:
User: The user object.
"""
try:
validate_password(validate_data["password"])
validate_password(validated_data["password"])
except ValidationError as err:
raise serializers.ValidationError(detail=err.messages) from err
password = validate_data.pop("password", None)
instance = self.Meta.model(**validate_data)
password = validated_data.pop("password", None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance

def update(self, instance, validate_data) -> UserProfileModel:
def update(self, instance, validated_data) -> UserProfileModel:
"""
Method to update a user.
Expand All @@ -116,7 +113,7 @@ def update(self, instance, validate_data) -> UserProfileModel:
Args:
instance: The user object.
validate_data: The data to validate.
validated_data: The data to validate.
Returns:
User: The updated user object.
Expand All @@ -125,7 +122,7 @@ def update(self, instance, validate_data) -> UserProfileModel:
serializers.ValidationError: If the password is the same as the current password.
"""
for attr, value in validate_data.items():
for attr, value in validated_data.items():
if attr == "password" and value is not None:
if instance.check_password(value):
raise serializers.ValidationError(detail="New password must be different from the current password.")
Expand All @@ -141,3 +138,8 @@ def update(self, instance, validate_data) -> UserProfileModel:
setattr(instance, attr, value)
instance.save()
return instance

class ConfirmEmailSerializer(serializers.ModelSerializer):
class Meta:
model = ConfirmEmail
fields = '__all__'
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ def login(self, request):
if "error" in response_message:
status_code = response_message.get("status_code")
response_message = response.json()
else:
status_code = status.HTTP_200_OK
else:
response_message = {"detail": "User is Inactive"}
status_code = status.HTTP_401_UNAUTHORIZED
Expand Down
93 changes: 52 additions & 41 deletions Backend/user_service/user_service/user_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.contrib.auth.hashers import check_password, make_password
from rest_framework.exceptions import ValidationError
from .models import UserProfileModel, FriendRequest
from .models import UserProfileModel, FriendRequest, ConfirmEmail
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.decorators import parser_classes
from django.utils.timezone import now
from .serializers import UserSerializer, FriendSerializer
from django.utils.timezone import now, timedelta
from .serializers import UserSerializer, FriendSerializer, ConfirmEmailSerializer
from django.core.mail import send_mail
from django.conf import settings
from django.db.models import Q
Expand Down Expand Up @@ -185,50 +185,50 @@ class RegisterViewSet(viewsets.ViewSet):
"""
permission_classes = [AllowAny]

def send_email(self, user, otp):
def send_email(self, email, otp):
send_mail(
'Email verification code',
f'Your verification code is: {otp}',
settings.EMAIL_HOST_USER,
[user.email],
[email],
fail_silently=False,
)

def send_email_otp(self, request) -> Response:
username = request.data.get("username")
try:
user_obj = get_object_or_404(UserProfileModel, username = username)
response_message = {}
status_code = status.HTTP_200_OK
if user_obj is not None:
email = request.data.get("email")
response_message = {}
status_code = status.HTTP_200_OK
if email is not None:
try:
email_obj, create = ConfirmEmail.objects.get_or_create(user_email=email)
otp = generate_secret()
user_obj.otp = make_password(str(otp))
user_obj.otp_expiry_time = now()
user_obj.save()
self.send_email(user_obj, otp)
email_obj.otp = make_password(str(otp))
email_obj.otp_expiry_time = now() + timedelta(minutes=1)
email_obj.save()
self.send_email(email_obj.user_email, otp)
response_message = {"detail":"Email verification code sent to your email"}
else:
response_message = {"error": "email field required"}
status_code = status.HTTP_404_NOT_FOUND
except Exception as err:
response_message = {"error": str(err)}
status_code = status.HTTP_400_BAD_REQUEST
except Exception as err:
response_message = {"error": str(err)}
status_code = status.HTTP_400_BAD_REQUEST
else:
response_message = {"error": "email field required"}
status_code = status.HTTP_404_NOT_FOUND
return Response(response_message, status=status_code)

def verify_email_otp(self, request) -> Response:
response_message = {}
status_code = status.HTTP_200_OK
username = request.data.get('username')
if username is not None:
user = get_object_or_404(UserProfileModel, username = username)
email = request.data.get('email')
if email is not None:
email_obj = get_object_or_404(ConfirmEmail, user_email = email)
otp = request.data.get('otp')
if otp is not None and user.otp is not None and user.otp_expiry_time is not None:
if check_password(str(otp), user.otp):
if user.otp_expiry_time > now():
if otp is not None and email_obj.otp is not None and email_obj.otp_expiry_time is not None:
if check_password(str(otp), email_obj.otp):
if email_obj.otp_expiry_time > now():
response_message = {"detail":"Email verified"}
user.otp = None
user.otp_expiry_time = None
user.save()
email_obj.otp = None
email_obj.otp_expiry_time = None
email_obj.save()
else:
response_message = {"error":"otp expired"}
status_code = status.HTTP_401_UNAUTHORIZED
Expand All @@ -239,7 +239,7 @@ def verify_email_otp(self, request) -> Response:
response_message = {'otp field required'}
status_code = status.HTTP_400_BAD_REQUEST
else:
response_message = {'error': 'user field required'}
response_message = {'error': 'email field required'}
status_code = status.HTTP_400_BAD_REQUEST
return Response(response_message, status=status_code)

Expand All @@ -254,21 +254,32 @@ def create_user(self, request) -> Response:
Returns:
Response: The response object containing the user data.
"""
response_message = {}
status_code = status.HTTP_201_CREATED
try:
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
if serializer.errors:
data = serializer.errors
if "email" in data:
data["email"] = ["A user with that email already exists."]
return Response({"error":data}, status=status.HTTP_400_BAD_REQUEST)
email = request.data.get("email")
email_obj = get_object_or_404(ConfirmEmail, user_email = email)
if email_obj is not None:
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
response_message = serializer.data
if serializer.errors:
data = serializer.errors
if "email" in data:
data["email"] = ["A user with that email already exists."]
response_message = {"error":data},
status_code=status.HTTP_400_BAD_REQUEST
except Http404:
response_message = {"error": "You have not verified your email yet!"}
status_code = status.HTTP_401_UNAUTHORIZED
except ValidationError as err:
item_lists = []
for item in err.detail:
item_lists.append(item)
return Response({'error': item_lists}, status=status.HTTP_400_BAD_REQUEST)
response_message = {'error': item_lists}
status_code=status.HTTP_400_BAD_REQUEST
return Response(response_message, status = status_code)

class FriendsViewSet(viewsets.ViewSet):
authentication_classes = [JWTAuthentication]
Expand Down

0 comments on commit b5fb47d

Please sign in to comment.