-
Notifications
You must be signed in to change notification settings - Fork 0
Add DRF User Registration with Custom Password Validation #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
d694238
backend auth implemented
karastoyanov 126dd16
add debug-tool-bar
Stunxzz a56646c
appUser
Stunxzz 0be88a6
Create register functionality:
karastoyanov 99521ff
update register url
karastoyanov a2a4bff
removed .idea PyCharm dir
karastoyanov 9def192
Added test cases for user registration
karastoyanov feeba93
create password validator
Stunxzz 2aa62e0
Update requirements for Python venv
karastoyanov 57ceff2
Merge branch 'feature/django-register' of github.com:Skill-Forge-Proj…
karastoyanov bf5b794
update unit tests
karastoyanov 4703e3b
Update userauth/validators.py
karastoyanov 04a27ef
Update skill_forge/settings.py
karastoyanov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,8 @@ | ||
| asgiref==3.9.1 | ||
| Django==5.2.5 | ||
| django-cors-headers==4.7.0 | ||
| django-debug-toolbar==6.0.0 | ||
| djangorestframework==3.16.1 | ||
| djangorestframework_simplejwt==5.5.1 | ||
| PyJWT==2.10.1 | ||
| sqlparse==0.5.3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,4 +3,4 @@ | |
|
|
||
| class AuthConfig(AppConfig): | ||
| default_auto_field = 'django.db.models.BigAutoField' | ||
| name = 'auth' | ||
| name = 'userauth' | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| from django.contrib.auth.models import AbstractUser | ||
| from django.db import models | ||
| from django.utils.translation import gettext_lazy as _ | ||
|
|
||
| class AppUser(AbstractUser): | ||
| email = models.EmailField(_('email address'), unique=True) | ||
|
|
||
| USERNAME_FIELD = 'email' | ||
| REQUIRED_FIELDS = ['username'] | ||
|
|
||
| class Meta: | ||
| db_table = "app_user" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| from django.contrib.auth.password_validation import validate_password | ||
| from rest_framework import serializers | ||
| from userauth.models import AppUser | ||
|
|
||
| class AppUserSerializer(serializers.ModelSerializer): | ||
| """ | ||
| Serializer for registering a new user. | ||
|
|
||
| Validates and creates a new user using Django's built-in User model. | ||
| Password is write-only and securely hashed before saving. | ||
| """ | ||
|
|
||
| password = serializers.CharField(write_only=True, required=True) | ||
| password2 = serializers.CharField(write_only=True, required=True) | ||
|
|
||
| class Meta: | ||
| model = AppUser | ||
| fields = ['id', 'username', 'password', 'password2', 'email', 'first_name', 'last_name', 'is_staff'] | ||
| read_only_fields = ['id', 'is_staff'] | ||
|
|
||
| def validate_password(self, value): | ||
| validate_password(value, self.instance) | ||
| return value | ||
|
|
||
| def validate(self, attrs): | ||
| if attrs['password'] != attrs["password2"]: | ||
| raise serializers.ValidationError({"password2": "Passwords do not match."}) | ||
| return attrs | ||
|
|
||
| def create(self, validated_data): | ||
| user = AppUser( | ||
| username=validated_data['username'], | ||
| email=validated_data['email'], | ||
| first_name=validated_data.get('first_name', ''), | ||
| last_name=validated_data.get('last_name', ''), | ||
| ) | ||
| user.set_password(validated_data['password']) | ||
| user.save() | ||
| return user | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| from django.urls import reverse | ||
| from rest_framework.test import APITestCase | ||
| from rest_framework import status | ||
| from .models import AppUser | ||
|
|
||
| class RegistrationTestCase(APITestCase): | ||
| """ | ||
| Test suite for AppUser registration endpoint. | ||
| """ | ||
|
|
||
| def setUp(self): | ||
| """ | ||
| Set up test data and endpoint URL. | ||
| """ | ||
| self.register_url = reverse('register') | ||
| self.user_data = { | ||
| 'email': 'test@example.com', | ||
| 'username': 'testuser', | ||
| 'password': 'Password13579!!!', | ||
| 'password2': 'Password13579!!!', | ||
| 'first_name': 'Test', | ||
| 'last_name': 'User' | ||
| } | ||
|
|
||
| def test_user_registration(self): | ||
| """ | ||
| Test successful registration of a new AppUser. | ||
| """ | ||
| response = self.client.post(self.register_url, self.user_data, format='json') | ||
| self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||
| self.assertEqual(AppUser.objects.count(), 1) | ||
| self.assertEqual(AppUser.objects.get().email, 'test@example.com') | ||
|
|
||
| def test_user_missing_email(self): | ||
| """ | ||
| Test registration fails when email is missing. | ||
| """ | ||
| data = self.user_data.copy() | ||
| data.pop('email') | ||
| response = self.client.post(self.register_url, data, format='json') | ||
| self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
| self.assertIn('email', response.data) | ||
|
|
||
| def test_user_missing_username(self): | ||
| """ | ||
| Test registration fails when username is missing. | ||
| """ | ||
| data = self.user_data.copy() | ||
| data.pop('username') | ||
| response = self.client.post(self.register_url, data, format='json') | ||
| self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
| self.assertIn('username', response.data) | ||
|
|
||
| def test_user_missing_password(self): | ||
| """ | ||
| Test registration fails when password is missing. | ||
| """ | ||
| data = self.user_data.copy() | ||
| data.pop('password') | ||
| response = self.client.post(self.register_url, data, format='json') | ||
| self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) | ||
| self.assertIn('password', response.data) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| from django.urls import path | ||
| from .views import RegisterView | ||
| # from rest_framework_simplejwt.views import ( | ||
| # TokenObtainPairView, | ||
| # TokenRefreshView, | ||
| # ) | ||
|
|
||
| urlpatterns = [ | ||
| path('register', RegisterView.as_view(), name='register'), | ||
| # path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'), | ||
| # path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import re | ||
| from django.core.exceptions import ValidationError | ||
| from django.utils.translation import gettext as _ | ||
|
|
||
| class CustomPasswordValidator: | ||
| def validate(self, password, user=None): | ||
|
|
||
| errors = [] | ||
| if len(password) < 8: | ||
| errors.append(_("Password must be at least 8 characters long.")) | ||
| if not re.search(r"[A-Z]", password): | ||
| errors.append(_("Password must contain at least one uppercase letter.")) | ||
| if not re.search(r"[a-z]", password): | ||
| errors.append(_("Password must contain at least one lowercase letter.")) | ||
| if not re.search(r"\d", password): | ||
| errors.append(_("Password must contain at least one digit.")) | ||
| if errors: | ||
| raise ValidationError(errors) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from rest_framework import generics | ||
| from .models import AppUser | ||
| from .serializers import AppUserSerializer | ||
|
|
||
|
|
||
| class RegisterView(generics.CreateAPIView): | ||
| """ | ||
| API view for user registration. | ||
|
|
||
| Accepts POST requests with username, email, and password. | ||
| Returns the created user data (excluding password). | ||
| """ | ||
|
|
||
| queryset = AppUser.objects.all() | ||
| serializer_class = AppUserSerializer |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.