Skip to content
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

implemented forgot password feature. #1680

Merged
merged 2 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions FusionIIIT/Fusion/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

# email of sender

EMAIL_HOST_USER = 'fusionmailservice@iiitdmj.ac.in'
EMAIL_HOST_USER = 'fusion@iiitdmj.ac.in'

EMAIL_PORT = 587
ACCOUNT_EMAIL_REQUIRED = True
Expand All @@ -68,7 +68,7 @@

ACCOUNT_EMAIL_SUBJECT_PREFIX = 'Fusion: '

DEFAULT_FROM_EMAIL = 'Fusion IIIT <fusionmailservice@iiitdmj.ac.in>'
DEFAULT_FROM_EMAIL = 'Fusion IIIT <fusion@iiitdmj.ac.in>'

SERVER_EMAIL = '[email protected]'

Expand Down
32 changes: 32 additions & 0 deletions FusionIIIT/Fusion/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path

from applications.globals.views import RateLimitedPasswordResetView


urlpatterns = [
Expand Down Expand Up @@ -62,4 +65,33 @@
url(r'^recruitment/', include('applications.recruitment.urls')),
url(r'^examination/', include('applications.examination.urls')),
url(r'^otheracademic/', include('applications.otheracademic.urls')),

path(
'password-reset/',
RateLimitedPasswordResetView.as_view(
template_name='registration/password_reset_form.html',
),
name='reset_password',
),
path(
'password-reset/done/',
auth_views.PasswordResetDoneView.as_view(
template_name='registration/password_reset_done.html'
),
name='password_reset_done',
),
path(
'reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(
template_name='registration/password_reset_confirm.html',
),
name='password_reset_confirm',
),
path(
'reset/done/',
auth_views.PasswordResetCompleteView.as_view(
template_name='registration/password_reset_complete.html'
),
name='password_reset_complete',
),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.1.5 on 2025-01-01 15:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('globals', '0002_moduleaccess'),
]

operations = [
migrations.CreateModel(
name='PasswordResetTracker',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254, unique=True)),
('last_reset', models.DateTimeField(blank=True, null=True)),
],
),
]
8 changes: 8 additions & 0 deletions FusionIIIT/applications/globals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,11 @@ class ModuleAccess(models.Model):

def __str__(self):
return self.designation


class PasswordResetTracker(models.Model):
email = models.EmailField(unique=True)
last_reset = models.DateTimeField(null=True, blank=True)

def __str__(self):
return self.email
39 changes: 39 additions & 0 deletions FusionIIIT/applications/globals/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,45 @@
from .models import *
from applications.hostel_management.models import (HallCaretaker,HallWarden)


from django.contrib.auth.views import PasswordResetView
from django.shortcuts import render
from django.utils.timezone import now
from datetime import timedelta
from .models import PasswordResetTracker

class RateLimitedPasswordResetView(PasswordResetView):
template_name = 'registration/password_reset_form.html' # Customize as needed

def post(self, request, *args, **kwargs):
email = request.POST.get('email')
if not email:
return self.form_invalid(self.get_form()) # Default behavior for invalid forms

# Check if the email exists in the tracker table
tracker, created = PasswordResetTracker.objects.get_or_create(email=email)

# Enforce rate limiting: Check if the reset was within the last 24 hours
if tracker.last_reset and now() - tracker.last_reset < timedelta(days=1):
# Pass error message to the template
return render(
request,
self.template_name,
{
'form': self.get_form(), # Include the form for re-rendering
'error_message': "Password can only be reset once every 24 hours.",
},
)

# Update the tracker with the current timestamp
tracker.last_reset = now()
tracker.save()

# Proceed with the standard password reset process
return super().post(request, *args, **kwargs)



def index(request):
context = {}
if(str(request.user)!="AnonymousUser"):
Expand Down
5 changes: 5 additions & 0 deletions FusionIIIT/templates/account/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
</div> -->
</form>

<div class="ui bottom attached warning message">
<i class="icon help"></i>
<a href="/password-reset">Forgot Password</a>.
</div>

<div class="box-header with-border">
{% get_providers as socialaccount_providers %}
</div>
Expand Down
13 changes: 13 additions & 0 deletions FusionIIIT/templates/registration/password_reset_complete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Reset Successful</title>
</head>
<body>
<h2>Password Reset Successful</h2>
<p>Your password has been reset successfully. You can now log in with your new password.</p>
<p><a href="/accounts/login/">Log In</a></p>
</body>
</html>
63 changes: 63 additions & 0 deletions FusionIIIT/templates/registration/password_reset_confirm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{% extends "account/base.html" %}
{% block content %}
{% load i18n %}
{% load account socialaccount %}
{% load static semanticui %}

<!-- <div class="content">
<img class="ui rounded centered small image"
src="{% static 'globals/img/user.png' %}"
style="margin-top: -27.5%; margin-left: 27.5%;"
alt="User Image">
<div class="ui huge centered header">
Login
</div>
</div> -->

<div class="content">
<div>
<h2 style="margin-bottom: 10px; text-align: center;">Set New Password</h2>
</div>
<form method="POST" class="ui form" style="margin-top: 2.5%;margin-bottom: 2.5%;">
{% csrf_token %}
{{ form.as_p }}
<button class = "fluid ui primary button" type="submit" style="margin-top: 20px;margin-bottom:10px">Reset Password</button>
</form>
</div>


{% if form.errors %}
<div id="error-popup" class="ui modal">
<div class="header">Reset Failed</div>
<div class="content">
<ul>
{% for field in form %}
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
<div class="actions">
<div class="ui red ok button">OK</div>
</div>
</div>
{% endif %}

<script>
document.addEventListener('DOMContentLoaded', function () {
if (document.getElementById('error-popup')) {
$('#error-popup').modal({
onApprove: function() {
$('#error-popup').modal('hide');
}
}).modal('show');
}
});
</script>


{% endblock %}
13 changes: 13 additions & 0 deletions FusionIIIT/templates/registration/password_reset_done.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Password Reset Sent</title>
</head>
<body>
<h2>Check Your Email</h2>
<p>If your email address is associated with an account, you will receive a password reset email shortly.</p>
<p><a href="/">Back to Home</a></p>
</body>
</html>
55 changes: 55 additions & 0 deletions FusionIIIT/templates/registration/password_reset_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% extends "account/base.html" %}
{% block content %}
{% load i18n %}
{% load account socialaccount %}
{% load static semanticui %}

<!-- <div class="content">
<img class="ui rounded centered small image"
src="{% static 'globals/img/user.png' %}"
style="margin-top: -27.5%; margin-left: 27.5%;"
alt="User Image">
<div class="ui huge centered header">
Login
</div>
</div> -->

<div class="content">
<div>
<h2 style="margin-bottom: 10px; text-align: center;">Forgot Your Password?</h2>
<p style="text-align: center;">Enter your email address below, and we'll send you a link to reset your password.</p>
</div>
<form method="POST" class="ui form" style="margin-top: 2.5%;margin-bottom: 2.5%;">
{% csrf_token %}
{{ form.as_p }}
<button class = "fluid ui primary button" type="submit" style="margin-top: 20px;margin-bottom:10px">Send Reset Link</button>
</form>
</div>

{% if error_message %}
<div id="error-popup" class="ui modal">
<div class="header">Reset Failed</div>
<div class="content">
<ul>
<li>{{ error_message }}</li>
</ul>
</div>
<div class="actions">
<div class="ui red ok button">OK</div>
</div>
</div>
{% endif %}

<script>
document.addEventListener('DOMContentLoaded', function () {
if (document.getElementById('error-popup')) {
$('#error-popup').modal({
onApprove: function() {
$('#error-popup').modal('hide');
}
}).modal('show');
}
});
</script>

{% endblock %}