-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat(backend): Added mail verification * feat: Added a frontend page to activate and redirect * feat: added tests for mail verification * fix: nits * fix: minor mistake * feat: limit to post * fix: tests * fix: test assert inactive acc
- Loading branch information
Showing
12 changed files
with
183 additions
and
12 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
|
@@ -5,3 +5,4 @@ EMAIL_HOST= | |
EMAIL_HOST_USER= | ||
EMAIL_HOST_PASSWORD= | ||
DJANGO_ADMINS= | ||
FRONTEND_URL= |
18 changes: 18 additions & 0 deletions
18
backend/accounts/migrations/0002_alter_account_is_active.py
This file contains 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 @@ | ||
# Generated by Django 4.2 on 2024-07-05 08:18 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('accounts', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='account', | ||
name='is_active', | ||
field=models.BooleanField(default=False), | ||
), | ||
] |
This file contains 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 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,7 @@ | ||
{% autoescape off %} | ||
Hi {{ user.username }}, | ||
|
||
Please click on the link below to confirm your registration: | ||
|
||
{{ protocol }}://{{ domain }}/activate?uuid={{uuid}}&token={{token}} | ||
{% endautoescape %} |
This file contains 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,7 +1,11 @@ | ||
import pytest | ||
from rest_framework.test import APIClient | ||
from django.utils.encoding import force_bytes | ||
from django.utils.http import urlsafe_base64_encode | ||
from rest_framework import status | ||
from rest_framework.test import APIClient | ||
|
||
from .models import Account | ||
from .token import account_activation_token | ||
|
||
account_data = { | ||
"email": "[email protected]", | ||
|
@@ -40,6 +44,9 @@ def testCreateAccountView_validAccountDetails_accountCreationSuccesful(): | |
assert 'password' not in response.data | ||
assert Account.objects.get().password != account_data['password'] | ||
|
||
# check that the account is inactive initially | ||
assert not Account.objects.get().is_active | ||
|
||
|
||
@pytest.mark.django_db | ||
@pytest.mark.parametrize('field', ['email', 'password', 'first_name', 'last_name']) | ||
|
@@ -72,6 +79,37 @@ def testCreateAccountView_invalidOrDuplicateEmail_returnsBadRequest(new_email): | |
assert response.status_code == status.HTTP_400_BAD_REQUEST | ||
|
||
|
||
@pytest.mark.django_db | ||
def testActivate_invalidTokenOrUid_returnsBadRequest(): | ||
# Arrange | ||
client = APIClient() | ||
account = Account.objects.create(**account_data) | ||
token = account_activation_token.make_token(account) | ||
uid = urlsafe_base64_encode(force_bytes(account.email)) | ||
|
||
# Act | ||
response = client.post(f'/api/accounts/activate/{uid}/malformed{token}') | ||
print(response.content,response) | ||
# Assert | ||
assert response.status_code == status.HTTP_400_BAD_REQUEST | ||
assert not Account.objects.get().is_active | ||
|
||
|
||
@pytest.mark.django_db | ||
def testActivate_validTokenAndUid_accountActivated(): | ||
# Arrange | ||
client = APIClient() | ||
account = Account.objects.create(**account_data) | ||
token = account_activation_token.make_token(account) | ||
uid = urlsafe_base64_encode(force_bytes(account.email)) | ||
|
||
# Act | ||
response = client.post(f'/api/accounts/activate/{uid}/{token}') | ||
|
||
# Assert | ||
assert response.status_code == status.HTTP_200_OK | ||
assert Account.objects.get().is_active | ||
|
||
@pytest.mark.django_db | ||
def testRetrieveLoggedInAccountView_userLoggedIn_returnsAccountDetails(api_client): | ||
# Act | ||
|
@@ -119,4 +157,4 @@ def testUpdateLogInAccountView_userNotLoggedIn_returnsUnauthorized(): | |
response = client.put('/api/accounts/me/', {}, format='json') | ||
|
||
# Assert | ||
assert response.status_code == status.HTTP_401_UNAUTHORIZED | ||
assert response.status_code == status.HTTP_401_UNAUTHORIZED |
This file contains 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,9 @@ | ||
from django.contrib.auth.tokens import PasswordResetTokenGenerator | ||
|
||
class AccountActivationTokenGenerator(PasswordResetTokenGenerator): | ||
def _make_hash_value(self, user, timestamp): | ||
return ( | ||
str(user.email) + str(timestamp) + str(user.is_active) | ||
) | ||
|
||
account_activation_token = AccountActivationTokenGenerator() |
This file contains 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,7 +1,8 @@ | ||
from django.urls import path | ||
from .views import CreateAccountView, RetrieveUpdateLoggedInAccountView | ||
from .views import CreateAccountView, RetrieveUpdateLoggedInAccountView,ActivateAccountView | ||
|
||
urlpatterns = [ | ||
path('', CreateAccountView.as_view()), | ||
path('me/', RetrieveUpdateLoggedInAccountView.as_view()), | ||
path('activate/<uidb64>/<token>', ActivateAccountView.as_view(), name='activate'), | ||
] |
This file contains 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 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 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 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,44 @@ | ||
<template> | ||
<div class="flex flex-col items-center justify-center h-[80svh] gap-4"> | ||
<p class="font-semibold text-4xl">{{ activationStatus }}</p> | ||
</div> | ||
</template> | ||
|
||
<script setup> | ||
import { ref, onMounted } from 'vue'; | ||
import { useRouter } from "vue-router"; | ||
const config = useRuntimeConfig(); | ||
const activationStatus = ref(''); | ||
const router = useRouter(); | ||
async function activateUser() { | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const uuid = urlParams.get('uuid'); | ||
const token = urlParams.get('token'); | ||
let status = ''; | ||
try { | ||
const response = await fetch( | ||
`${config.public.API_BASE_URL}/api/accounts/activate/${uuid}/${token}`, | ||
{ method: "POST" } | ||
); | ||
status = await response.text(); | ||
if (response.status === 200) { | ||
router.push({ path: '/signin', query: { msg: 'account activated' } }); | ||
} else { | ||
status = 'Activation failed'; | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
status = 'Activation failed'; | ||
} | ||
return status; | ||
} | ||
onMounted(async () => { | ||
const status = await activateUser(); | ||
activationStatus.value = status +' !!'; | ||
}); | ||
</script> |
This file contains 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