Finally got mail working for good
This commit is contained in:
parent
d549897186
commit
89f971117f
20 changed files with 135 additions and 30 deletions
|
@ -30,6 +30,8 @@ class BaseMailProvider:
|
|||
for header, value in headers.items():
|
||||
if value:
|
||||
message.add_header(header, value)
|
||||
|
||||
message.set_charset("base64")
|
||||
return self.send_message(message)
|
||||
|
||||
class SMTPMailProvider(BaseMailProvider):
|
||||
|
@ -43,4 +45,4 @@ class SMTPMailProvider(BaseMailProvider):
|
|||
return "SMTP Mail"
|
||||
|
||||
def send_message(self, message):
|
||||
return self.smtp.send_message(message)
|
||||
return self.smtp.send_message(message, rcpt_options=['NOTIFY=SUCCESS,DELAY,FAILURE'])
|
||||
|
|
2
core/exceptions/mail.py
Normal file
2
core/exceptions/mail.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
class NoSuchTemplate(ValueError):
|
||||
pass
|
|
@ -1,2 +1,11 @@
|
|||
from core.helpers.mail import get_template
|
||||
from core.helpers.urls import relative_to_absolute as reltoabs
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from dbsettings.functions import getValue
|
||||
|
||||
def generate_pwreset_mail(user, token):
|
||||
pass
|
||||
link = reltoabs(reverse("pwreset", kwargs={"pk": str(token.token)}))
|
||||
template = get_template("backend/auth/pwreset", first_name=user.first_name, link=link, sitename=getValue("core.title", "Expephalon"))
|
||||
return template
|
|
@ -1,8 +1,13 @@
|
|||
from core.modules.mail import providers
|
||||
from django.conf import settings
|
||||
|
||||
from core.modules.mail import providers, templates
|
||||
from core.tasks.mail import send_mail as send_mail_task
|
||||
from core.exceptions.mail import NoSuchTemplate
|
||||
|
||||
from dbsettings.functions import getValue
|
||||
|
||||
import os.path
|
||||
|
||||
def get_provider_by_name(name, fallback=True):
|
||||
return providers.get(name, None) or providers["smtp"]
|
||||
|
||||
|
@ -16,5 +21,16 @@ def send_mail(provider=get_default_provider(), **kwargs):
|
|||
def simple_send_mail(subject, content, recipients, cc=[], bcc=[], headers={}):
|
||||
return send_mail(subject=subject, content=content, recipients=recipients, cc=cc, bcc=bcc, headers=headers)
|
||||
|
||||
def fetch_templates(template_name):
|
||||
pass
|
||||
def get_template(template_name, format="txt", **kwargs):
|
||||
try:
|
||||
template = templates[template_name][format]
|
||||
except KeyError:
|
||||
raise NoSuchTemplate(f"No email template called {template_name} of format {format} loaded")
|
||||
|
||||
with open(template, "r") as templatefile:
|
||||
templatetext = templatefile.read()
|
||||
|
||||
for key, value in kwargs.items():
|
||||
templatetext = templatetext.replace('{§%s§}' % key, value)
|
||||
|
||||
return templatetext
|
6
core/helpers/urls.py
Normal file
6
core/helpers/urls.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from urllib.parse import urljoin
|
||||
|
||||
from dbsettings.functions import getValue
|
||||
|
||||
def relative_to_absolute(path, domain=getValue("core.base_url", "http://localhost:8000")):
|
||||
return urljoin(domain, path)
|
18
core/mixins/auth.py
Normal file
18
core/mixins/auth.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from django.contrib.auth.mixins import AccessMixin
|
||||
from django.contrib.messages import error
|
||||
|
||||
from core.models.profiles import AdminProfile
|
||||
|
||||
class AdminMixin(AccessMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
self.permission_denied_message = "You must be logged in to access this area."
|
||||
|
||||
else:
|
||||
try:
|
||||
AdminProfile.objects.get(user=request.user)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
except AdminProfile.DoesNotExist:
|
||||
self.permission_denied_message = "You must be an administrator to access this area."
|
||||
|
||||
return self.handle_no_permission()
|
|
@ -1,10 +1,28 @@
|
|||
from core.classes.mail import SMTPMailProvider
|
||||
|
||||
import importlib
|
||||
import pathlib
|
||||
import os.path
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
providers = { "smtp": SMTPMailProvider }
|
||||
templates = {}
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
for module in settings.EXPEPHALON_MODULES + [""]:
|
||||
for template in pathlib.Path(os.path.join(settings.BASE_DIR, module, "templates/mail/")).rglob("*.*"):
|
||||
if os.path.isfile(template):
|
||||
template_name = str(template).rsplit("templates/mail/")[-1].rsplit(".")[0]
|
||||
template_format = str(template).rsplit(".")[-1].lower()
|
||||
if not template_name in templates.keys():
|
||||
templates[template_name] = dict()
|
||||
if template_format in templates[template_name].keys():
|
||||
logger.warning("Mail Template %s, that was seen at %s, was also found at %s. Using latter.",
|
||||
template_name, templates[template_name][template_format], str(template))
|
||||
templates[template_name][template_format] = str(template)
|
||||
|
||||
for module in settings.EXPEPHALON_MODULES:
|
||||
try:
|
||||
|
|
|
@ -10,6 +10,7 @@ from core.views import (
|
|||
LogoutView,
|
||||
OTPValidatorView,
|
||||
PWResetView,
|
||||
PWRequestView,
|
||||
BackendNotImplementedView,
|
||||
AdminListView,
|
||||
AdminDeleteView,
|
||||
|
@ -29,7 +30,8 @@ URLPATTERNS.append(path('login/', LoginView.as_view(), name="login"))
|
|||
URLPATTERNS.append(path('login/otp/select/', OTPSelectorView.as_view(), name="otpselector"))
|
||||
URLPATTERNS.append(path('login/otp/validate/', OTPValidatorView.as_view(), name="otpvalidator"))
|
||||
URLPATTERNS.append(path('logout/', LogoutView.as_view(), name="logout"))
|
||||
URLPATTERNS.append(path('login/reset/', PWResetView.as_view(), name="pwreset"))
|
||||
URLPATTERNS.append(path('login/reset/', PWRequestView.as_view(), name="pwrequest"))
|
||||
URLPATTERNS.append(path('login/reset/<pk>/', PWResetView.as_view(), name="pwreset"))
|
||||
|
||||
# Base Backend URLs
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ from django.conf import settings
|
|||
from core.views.dbsettings import *
|
||||
from core.views.auth import *
|
||||
from core.views.profiles import *
|
||||
from core.views.generic import *
|
||||
from core.mixins.auth import AdminMixin
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
@ -16,7 +18,7 @@ class IndexView(TemplateView):
|
|||
context["title"] = "Home"
|
||||
return context
|
||||
|
||||
class DashboardView(TemplateView):
|
||||
class DashboardView(BackendTemplateView):
|
||||
template_name = f"{settings.EXPEPHALON_BACKEND}/index.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -24,10 +26,10 @@ class DashboardView(TemplateView):
|
|||
context["title"] = "Dashboard"
|
||||
return context
|
||||
|
||||
class BackendNotImplementedView(TemplateView):
|
||||
class BackendNotImplementedView(BackendTemplateView):
|
||||
template_name = f"{settings.EXPEPHALON_BACKEND}/notimplemented.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["title"] = "Oops!"
|
||||
return context
|
||||
return context
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.utils import timezone
|
|||
from core.forms import LoginForm, OTPSelectorForm, OTPVerificationForm, PWResetForm, PWRequestForm
|
||||
from core.models.auth import LoginSession, PWResetToken
|
||||
from core.helpers.otp import get_user_otps, get_otp_choices, get_otp_by_name
|
||||
from core.helpers.mail import send_mail
|
||||
from core.helpers.mail import simple_send_mail
|
||||
from core.helpers.auth import generate_pwreset_mail
|
||||
|
||||
from dbsettings.functions import getValue
|
||||
|
@ -164,7 +164,10 @@ class PWRequestView(FormView):
|
|||
try:
|
||||
user = get_user_model().objects.get(username=form.cleaned_data["email"])
|
||||
token = PWResetToken.objects.create(user=user)
|
||||
|
||||
finally:
|
||||
messages.success(self.request, "If a matching account was found, you should shortly receive an email containing password reset instructions. If you have not received this message after five minutes, please verify that you have entered the correct email address, or contact support.")
|
||||
return redirect("login")
|
||||
mail = generate_pwreset_mail(user, token)
|
||||
simple_send_mail("Password Reset", mail, user.email)
|
||||
except:
|
||||
raise
|
||||
# finally:
|
||||
# messages.success(self.request, "If a matching account was found, you should shortly receive an email containing password reset instructions. If you have not received this message after five minutes, please verify that you have entered the correct email address, or contact support.")
|
||||
# return redirect("login")
|
17
core/views/generic.py
Normal file
17
core/views/generic.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.views.generic import TemplateView, ListView, CreateView, FormView, DeleteView
|
||||
from core.mixins.auth import AdminMixin
|
||||
|
||||
class BackendTemplateView(AdminMixin, TemplateView):
|
||||
pass
|
||||
|
||||
class BackendListView(AdminMixin, ListView):
|
||||
pass
|
||||
|
||||
class BackendCreateView(AdminMixin, CreateView):
|
||||
pass
|
||||
|
||||
class BackendFormView(AdminMixin, FormView):
|
||||
pass
|
||||
|
||||
class BackendDeleteView(AdminMixin, DeleteView):
|
||||
pass
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf import settings
|
||||
from django.views.generic import FormView, ListView, DeleteView
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from core.models import AdminProfile
|
||||
from core.forms import AdminEditForm
|
||||
from core.views.generic import BackendFormView as FormView, BackendListView as ListView, BackendDeleteView as DeleteView
|
||||
|
||||
class AdminListView(ListView):
|
||||
template_name = f"{settings.EXPEPHALON_BACKEND}/profiles/index.html"
|
||||
|
|
|
@ -16,7 +16,6 @@ app.config_from_object('django.conf:settings', namespace='CELERY')
|
|||
# Load task modules from all registered Django app configs.
|
||||
app.autodiscover_tasks()
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def debug_task(self):
|
||||
print('Request: {0!r}'.format(self.request))
|
|
@ -1,5 +1,7 @@
|
|||
import os
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from expephalon.custom_settings import * # pylint: disable=unused-wildcard-import
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
|
@ -141,6 +143,14 @@ if MEMCACHED_LOCATION:
|
|||
CELERY_TASK_SERIALIZER = "pickle"
|
||||
CELERY_RESULT_SERIALIZER = "pickle"
|
||||
CELERY_ACCEPT_CONTENT = ['pickle']
|
||||
CELERY_CACHE_BACKEND = 'default'
|
||||
CELERY_RESULT_BACKEND = 'django-db'
|
||||
CELERY_CACHE_BACKEND = 'django-cache'
|
||||
CELERY_BROKER_URL = f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASS}@{RABBITMQ_LOCATION}/{RABBITMQ_VHOST}"
|
||||
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
|
||||
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
|
||||
CELERY_TASK_RESULT_EXPIRES = 12 * 60 * 60
|
||||
|
||||
# Auth URLs
|
||||
|
||||
LOGIN_REDIRECT_URL = reverse_lazy('dashboard')
|
||||
LOGIN_URL = reverse_lazy('login')
|
||||
LOGOUT_URL = reverse_lazy('logout')
|
|
@ -1,11 +0,0 @@
|
|||
Hi {{ first_name }},
|
||||
|
||||
Somebody (hopefully you) requested a new password for your {{ sitename }} account. If this was you, please click the following link to reset your password:
|
||||
|
||||
{{ link }}
|
||||
|
||||
If it was not you, you can ignore this message. The link will expire in 24 hours.
|
||||
|
||||
Best regards
|
||||
|
||||
Your {{ sitename }} Team
|
|
@ -11,3 +11,4 @@ git+https://kumig.it/kumisystems/django-dbsettings.git
|
|||
celery
|
||||
django-celery-results
|
||||
django-celery-beat
|
||||
python-memcached
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
|
||||
<div class="divider row"></div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
|
||||
<div class="ml-auto"><a href="{% url "pwrequest" %}" class="btn-lg btn btn-link">Recover Password</a>
|
||||
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
0
templates/mail/backend/auth/pwreset.html
Normal file
0
templates/mail/backend/auth/pwreset.html
Normal file
11
templates/mail/backend/auth/pwreset.txt
Normal file
11
templates/mail/backend/auth/pwreset.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
Hi {§first_name§},
|
||||
|
||||
Somebody (hopefully you) requested a new password for your {§sitename§} account. If this was you, please click the following link to reset your password:
|
||||
|
||||
{§link§}
|
||||
|
||||
If it was not you, you can ignore this message. The link will expire in 24 hours.
|
||||
|
||||
Best regards
|
||||
|
||||
Your {§sitename§} Team
|
Loading…
Reference in a new issue