From 5555b36b7ffe7a2b8fd4c11447aab03751e637bc Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 12:52:40 +0200 Subject: [PATCH 01/10] Fix CI by switching from mysql to postgres requirements install --- .github/workflows/black_linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black_linter.yml b/.github/workflows/black_linter.yml index ea8df899..58a9870f 100644 --- a/.github/workflows/black_linter.yml +++ b/.github/workflows/black_linter.yml @@ -25,7 +25,7 @@ jobs: python -m pip install --upgrade pip pip install black==19.3b pytest pip install click==8.0.2 - pip install -r app/requirements/mysql.txt + pip install -r app/requirements/postgres.txt - name: Analysing the code with pylint run: | black --check . From 414cd82c2f282e8ecfd054aeb34fec909bb7cdab Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 13:02:07 +0200 Subject: [PATCH 02/10] Remove obsolete HTML artefact --- app/templates/registration/login.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/templates/registration/login.html b/app/templates/registration/login.html index 0edbb81d..21782ba6 100644 --- a/app/templates/registration/login.html +++ b/app/templates/registration/login.html @@ -23,14 +23,6 @@ From 07e1702ed460ce087fd406121f5ea92770ddbb44 Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 13:04:10 +0200 Subject: [PATCH 03/10] Remove obsolete HTML artefact from signup template --- app/templates/registration/signup.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/templates/registration/signup.html b/app/templates/registration/signup.html index ccae27cd..78033b74 100644 --- a/app/templates/registration/signup.html +++ b/app/templates/registration/signup.html @@ -27,14 +27,6 @@ - - + + {% endblock %} \ No newline at end of file diff --git a/app/users/urls.py b/app/users/urls.py index f2cab5be..60cde21d 100644 --- a/app/users/urls.py +++ b/app/users/urls.py @@ -1,10 +1,11 @@ from django.urls import path -from .views import * +from .views import signup, change_password, user_info, activate, password_reset_request urlpatterns = [ path("signup/", signup, name="signup"), path("change_password/", change_password, name="change_password"), path("user_info/", user_info, name="user_info"), path("activate///", activate, name="activate"), + path("password_reset", password_reset_request, name="password_reset") ] diff --git a/app/users/views.py b/app/users/views.py index 27015bf8..463159e4 100644 --- a/app/users/views.py +++ b/app/users/views.py @@ -1,26 +1,27 @@ +from django.conf import settings +from django.contrib import messages +from django.contrib.auth import update_session_auth_hash, get_user_model from django.contrib.auth.decorators import login_required +from django.contrib.auth.forms import PasswordChangeForm, PasswordResetForm from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site -from django.core.mail import EmailMessage +from django.core.mail import EmailMessage, send_mail, BadHeaderError +from django.db.models import Q from django.http import HttpResponse from django.http.response import HttpResponseRedirect +from django.shortcuts import render, redirect from django.template.loader import render_to_string -from django.urls import reverse_lazy from django.urls.base import reverse from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.views.decorators.http import require_http_methods -from django.views.generic import CreateView, UpdateView -from django.contrib import messages -from django.contrib.auth import update_session_auth_hash, get_user_model -from django.contrib.auth.forms import PasswordChangeForm -from django.shortcuts import render, redirect from .forms import CustomUserCreationForm, CustomUserChangeForm +from .models import CustomUser -from .tokens import account_activation_token - +DEFAULT_FROM_EMAIL = settings.DEFAULT_FROM_EMAIL +EMAIL_HOST = settings.EMAIL_HOST UserModel = get_user_model() @@ -101,3 +102,34 @@ def change_password(request): else: form = PasswordChangeForm(request.user) return render(request, "registration/change_password.html", {"form": form}) + + +def password_reset_request(request): + if request.method == "POST": + password_reset_form = PasswordResetForm(request.POST) + if password_reset_form.is_valid(): + data = password_reset_form.cleaned_data['email'] + associated_users = CustomUser.objects.filter(Q(email=data)) + if associated_users.exists(): + for user in associated_users: + subject = "Password Reset Requested" + email_template_name = "registration/password_reset_email.txt" + c = { + "email": user.email, + 'domain': EMAIL_HOST, + 'site_name': 'open_plan', + "uid": urlsafe_base64_encode(force_bytes(user.pk)), + "user": user, + 'token': default_token_generator.make_token(user), + 'protocol': 'http', + } + email = render_to_string(email_template_name, c) + try: + send_mail(subject, email, DEFAULT_FROM_EMAIL, [user.email], + fail_silently=False) + except BadHeaderError: + return HttpResponse('Invalid header found.') + return redirect("/password_reset/done/") + password_reset_form = PasswordResetForm() + return render(request=request, template_name="registration/password_reset_form.html", + context={"password_reset_form": password_reset_form}) From 2a86071b81e9e89920aac26fe74bac4c2ee52ca5 Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 15:15:00 +0200 Subject: [PATCH 06/10] Make black happy --- app/users/urls.py | 2 +- app/users/views.py | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/users/urls.py b/app/users/urls.py index 60cde21d..ffc674b2 100644 --- a/app/users/urls.py +++ b/app/users/urls.py @@ -7,5 +7,5 @@ path("change_password/", change_password, name="change_password"), path("user_info/", user_info, name="user_info"), path("activate///", activate, name="activate"), - path("password_reset", password_reset_request, name="password_reset") + path("password_reset", password_reset_request, name="password_reset"), ] diff --git a/app/users/views.py b/app/users/views.py index 463159e4..41d873da 100644 --- a/app/users/views.py +++ b/app/users/views.py @@ -108,7 +108,7 @@ def password_reset_request(request): if request.method == "POST": password_reset_form = PasswordResetForm(request.POST) if password_reset_form.is_valid(): - data = password_reset_form.cleaned_data['email'] + data = password_reset_form.cleaned_data["email"] associated_users = CustomUser.objects.filter(Q(email=data)) if associated_users.exists(): for user in associated_users: @@ -116,20 +116,28 @@ def password_reset_request(request): email_template_name = "registration/password_reset_email.txt" c = { "email": user.email, - 'domain': EMAIL_HOST, - 'site_name': 'open_plan', + "domain": EMAIL_HOST, + "site_name": "open_plan", "uid": urlsafe_base64_encode(force_bytes(user.pk)), "user": user, - 'token': default_token_generator.make_token(user), - 'protocol': 'http', + "token": default_token_generator.make_token(user), + "protocol": "http", } email = render_to_string(email_template_name, c) try: - send_mail(subject, email, DEFAULT_FROM_EMAIL, [user.email], - fail_silently=False) + send_mail( + subject, + email, + DEFAULT_FROM_EMAIL, + [user.email], + fail_silently=False, + ) except BadHeaderError: - return HttpResponse('Invalid header found.') + return HttpResponse("Invalid header found.") return redirect("/password_reset/done/") password_reset_form = PasswordResetForm() - return render(request=request, template_name="registration/password_reset_form.html", - context={"password_reset_form": password_reset_form}) + return render( + request=request, + template_name="registration/password_reset_form.html", + context={"password_reset_form": password_reset_form}, + ) From b923d7a882cddfddaab0eb2b823fed4429623ac9 Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Thu, 27 Oct 2022 13:22:11 +0200 Subject: [PATCH 07/10] Default DEBUG to False (security) and use console email backend only in debug mode --- README.md | 1 + app/epa/settings.py | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4db1b2c6..b00671ad 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ SQL_USER= SQL_PASSWORD= SQL_HOST=localhost SQL_PORT=5432 +DEBUG=(True|False) ``` 8. Add an environment variable `MVS_HOST_API` and set the url of the simulation server you wish to use for your models 9. Execute the `local_setup.sh` file (`. local_setup.sh` on linux/mac `bash local_setup.sh` on windows) you might have to make it executable first. Answer yes to the question diff --git a/app/epa/settings.py b/app/epa/settings.py index 33702f4b..e4b8e240 100644 --- a/app/epa/settings.py +++ b/app/epa/settings.py @@ -11,11 +11,11 @@ """ import ast import os -from django.contrib.messages import constants as messages +from django.contrib.messages import constants as messages # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = ast.literal_eval(os.getenv("DEBUG", "True")) +DEBUG = ast.literal_eval(os.getenv("DEBUG", "False")) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -40,7 +40,6 @@ "EPA_SECRET_KEY", "v@p9^=@lc3#1u_xtx*^xhrv0l3li1(+8ik^k@g-_bzmexb0$7n" ) - ALLOWED_HOSTS = ["*"] # Application definition @@ -64,7 +63,6 @@ if DEBUG is True: INSTALLED_APPS.append("sass_processor") - MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -125,7 +123,6 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField" - # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators @@ -155,7 +152,6 @@ USE_TZ = False - # Other configs AUTH_USER_MODEL = "users.CustomUser" @@ -166,7 +162,10 @@ CRISPY_TEMPLATE_PACK = "bootstrap4" -EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +if DEBUG is True: + EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +else: + EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" MAILER_EMAIL_BACKEND = EMAIL_BACKEND DEFAULT_FROM_EMAIL = "noreply@elandh2020.eu" From c2ef4cc794c3e1cdfb1d199f21bf92f53d9df7a0 Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 16:22:36 +0200 Subject: [PATCH 08/10] Remove unused tokens module --- app/users/tokens.py | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 app/users/tokens.py diff --git a/app/users/tokens.py b/app/users/tokens.py deleted file mode 100644 index 8fc124a2..00000000 --- a/app/users/tokens.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.contrib.auth.tokens import PasswordResetTokenGenerator - - -class AccountActivationTokenGenerator(PasswordResetTokenGenerator): - def _make_hash_value(self, user, timestamp): - return user.pk + timestamp + user.is_active - - -account_activation_token = AccountActivationTokenGenerator() From 5d8e04c69e440783372c5fcf8b9199f3d4ff678c Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Tue, 25 Oct 2022 17:11:12 +0200 Subject: [PATCH 09/10] Re-enable email confirmation functionality #192 --- app/users/forms.py | 2 -- app/users/models.py | 1 - app/users/views.py | 9 +++------ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/users/forms.py b/app/users/forms.py index cd90f21a..3f874167 100644 --- a/app/users/forms.py +++ b/app/users/forms.py @@ -1,7 +1,5 @@ from django import forms from django.contrib.auth.forms import UserCreationForm, UserChangeForm -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Field from .models import CustomUser diff --git a/app/users/models.py b/app/users/models.py index dcfbbf64..659ac76b 100644 --- a/app/users/models.py +++ b/app/users/models.py @@ -1,5 +1,4 @@ from django.contrib.auth.models import AbstractUser -from django.db import models class CustomUser(AbstractUser): diff --git a/app/users/views.py b/app/users/views.py index 41d873da..52e5f561 100644 --- a/app/users/views.py +++ b/app/users/views.py @@ -16,7 +16,6 @@ from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.views.decorators.http import require_http_methods - from .forms import CustomUserCreationForm, CustomUserChangeForm from .models import CustomUser @@ -28,11 +27,9 @@ def signup(request): if request.method == "POST": form = CustomUserCreationForm(request.POST) - # print(form.errors.as_data()) if form.is_valid(): user = form.save(commit=False) - # user.is_active = False - user.is_active = True + user.is_active = False user.save() current_site = get_current_site(request) mail_subject = "Activate your account." @@ -47,8 +44,8 @@ def signup(request): ) to_email = form.cleaned_data.get("email") email = EmailMessage(mail_subject, message, to=[to_email]) - # email.send() - # return HttpResponse('Please confirm your email address to complete the registration') + email.send() + return HttpResponse('Please confirm your email address to complete the registration') return HttpResponseRedirect(reverse("login")) else: form = CustomUserCreationForm() From 550941ac9023a796b329528368a3ae2379a1514c Mon Sep 17 00:00:00 2001 From: Alexis Michaltsis Date: Thu, 27 Oct 2022 12:50:47 +0200 Subject: [PATCH 10/10] Use messaging system, not plain text for info and success messages, for email verification process --- app/users/views.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/users/views.py b/app/users/views.py index 52e5f561..e88ff55d 100644 --- a/app/users/views.py +++ b/app/users/views.py @@ -9,10 +9,8 @@ from django.core.mail import EmailMessage, send_mail, BadHeaderError from django.db.models import Q from django.http import HttpResponse -from django.http.response import HttpResponseRedirect from django.shortcuts import render, redirect from django.template.loader import render_to_string -from django.urls.base import reverse from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.views.decorators.http import require_http_methods @@ -45,8 +43,8 @@ def signup(request): to_email = form.cleaned_data.get("email") email = EmailMessage(mail_subject, message, to=[to_email]) email.send() - return HttpResponse('Please confirm your email address to complete the registration') - return HttpResponseRedirect(reverse("login")) + messages.info(request, "Please confirm your email address to complete the registration") + return redirect("home") else: form = CustomUserCreationForm() return render(request, "registration/signup.html", {"form": form}) @@ -61,11 +59,11 @@ def activate(request, uidb64, token): if user is not None and default_token_generator.check_token(user, token): user.is_active = True user.save() - return HttpResponse( - "Thank you for your email confirmation. Now you can login your account." - ) + messages.success(request, "Thank you for your email confirmation. Now you can login your account.") + return redirect("login") else: return HttpResponse("Activation link is invalid!") + return redirect("home") @login_required