Skip to content

Commit

Permalink
Ask the password to access the webmail
Browse files Browse the repository at this point in the history
  • Loading branch information
Spitfireap committed Aug 18, 2024
1 parent 48d3a5a commit 23f91d0
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 2 deletions.
8 changes: 8 additions & 0 deletions modoboa_webmail/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,3 +429,11 @@ def clean_mboxes_col_width(self):
_('Value must be a positive integer (> 0)')
)
return self.cleaned_data['mboxes_col_width']

class AskPassword(forms.Form):
"""Form to ask user password for the IMAP connection."""

password = forms.CharField(
label=_("password"), required=True,
widget=forms.PasswordInput(attrs={"class": "form-control"})
)
18 changes: 18 additions & 0 deletions modoboa_webmail/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""Misc. utilities."""
from functools import wraps

from django.shortcuts import redirect

from modoboa.lib.web_utils import NavigationParameters
from modoboa.lib.cryptutils import get_password


def decode_payload(encoding, payload):
Expand Down Expand Up @@ -40,3 +44,17 @@ def _store_page(self):
page = self.request.GET.get("page", None)
if page is not None:
self["page"] = int(page)


def need_password(*args, **kwargs):
"""Check if the session holds the user password for the IMAP connection.
"""
def decorator(f):
@wraps(f)
def wrapped_f(request, *args, **kwargs):
if get_password(request) is None:
return redirect("modoboa_webmail:get_plain_password")
return f(request, *args, **kwargs)
return wrapped_f
return decorator

18 changes: 18 additions & 0 deletions modoboa_webmail/templates/modoboa_webmail/ask_password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "fluid.html" %}

{% load i18n form_tags %}

{% block pagetitle %}{% trans "Password validation" %}{% endblock %}

{% block container_content %}
<h2 style="text-align: center">{% trans "Validate password" %} <br><small>{% trans "Please confirm your password" %}</small></h2>
<hr>
<form class="form-horizontal" method="POST" action="{% url 'modoboa_webmail:get_plain_password' %}"
id="uprefs_form">
{% csrf_token %}
{% render_form form %}
<div class="form-actions" style="margin-top: 15px;">
<button type="submit" class="btn btn-primary btn-lg col-sm-offset-4 col-sm-4" id="validate" name="validate">{% trans 'Validate' %}</button>
</div>
</form>
{% endblock container_content %}
1 change: 1 addition & 0 deletions modoboa_webmail/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@
path('attachments/', views.attachments, name="attachment_list"),
path('delattachment/', views.delattachment, name="attachment_delete"),
path('getattachment/', views.getattachment, name="attachment_get"),
path('password/', views.get_plain_password, name="get_plain_password")
]
55 changes: 53 additions & 2 deletions modoboa_webmail/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
from django.http import HttpResponse
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_str
from django.utils.translation import gettext as _, ngettext
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.gzip import gzip_page
from django.shortcuts import redirect

from django.contrib.auth.decorators import login_required

from modoboa.admin.lib import needs_mailbox
from modoboa.core.extensions import exts_pool
from django.contrib.auth.hashers import check_password
from modoboa.lib.cryptutils import encrypt, get_password
from modoboa.lib.exceptions import ModoboaException, BadRequest
from modoboa.lib.paginator import Paginator
from modoboa.lib.web_utils import (
Expand All @@ -27,7 +31,8 @@

from .exceptions import UnknownAction
from .forms import (
FolderForm, AttachmentForm, ComposeMailForm, ForwardMailForm
FolderForm, AttachmentForm, ComposeMailForm, ForwardMailForm,
AskPassword
)
from .lib import (
decode_payload, AttachmentUploadHandler,
Expand All @@ -36,6 +41,7 @@
ImapEmail, WebmailNavigationParameters, ReplyModifier, ForwardModifier,
get_imapconnector, IMAPconnector, separate_mailbox, rfc6266
)
from .lib.utils import need_password
from .templatetags import webmail_tags


Expand All @@ -46,6 +52,7 @@ def is_ajax(request):
@login_required
@needs_mailbox()
@gzip_page
@need_password()
def getattachment(request):
"""Fetch a message attachment
Expand Down Expand Up @@ -74,6 +81,7 @@ def getattachment(request):

@login_required
@needs_mailbox()
@need_password()
def move(request):
for arg in ["msgset", "to"]:
if arg not in request.GET:
Expand All @@ -87,6 +95,7 @@ def move(request):

@login_required
@needs_mailbox()
@need_password()
def delete(request):
mbox = request.GET.get("mbox", None)
selection = request.GET.getlist("selection[]", None)
Expand All @@ -105,6 +114,7 @@ def delete(request):

@login_required
@needs_mailbox()
@need_password()
def mark(request, name):
status = request.GET.get("status", None)
ids = request.GET.get("ids", None)
Expand Down Expand Up @@ -136,6 +146,7 @@ def _move_selection_to_folder(request, folder):

@login_required
@needs_mailbox()
@need_password()
def mark_as_junk(request):
"""Mark a message as SPAM."""
count = _move_selection_to_folder(
Expand All @@ -148,6 +159,7 @@ def mark_as_junk(request):

@login_required
@needs_mailbox()
@need_password()
def mark_as_not_junk(request):
"""Mark a message as not SPAM."""
count = _move_selection_to_folder(request, "INBOX")
Expand All @@ -159,6 +171,7 @@ def mark_as_not_junk(request):

@login_required
@needs_mailbox()
@need_password()
def empty(request):
"""Empty the trash folder."""
name = request.GET.get("name", None)
Expand All @@ -173,6 +186,7 @@ def empty(request):

@login_required
@needs_mailbox()
@need_password()
def folder_compress(request):
"""Compress a mailbox."""
name = request.GET.get("name", None)
Expand All @@ -185,6 +199,7 @@ def folder_compress(request):

@login_required
@needs_mailbox()
@need_password()
def newfolder(request, tplname="modoboa_webmail/folder.html"):
mbc = IMAPconnector(user=request.user.username,
password=request.session["password"])
Expand Down Expand Up @@ -218,6 +233,7 @@ def newfolder(request, tplname="modoboa_webmail/folder.html"):

@login_required
@needs_mailbox()
@need_password()
def editfolder(request, tplname="modoboa_webmail/folder.html"):
mbc = IMAPconnector(user=request.user.username,
password=request.session["password"])
Expand Down Expand Up @@ -275,6 +291,7 @@ def editfolder(request, tplname="modoboa_webmail/folder.html"):

@login_required
@needs_mailbox()
@need_password()
def delfolder(request):
name = request.GET.get("name", None)
if name is None:
Expand All @@ -289,6 +306,7 @@ def delfolder(request):
@login_required
@csrf_exempt
@needs_mailbox()
@need_password()
def attachments(request, tplname="modoboa_webmail/attachments.html"):
if request.method == "POST":
csuploader = AttachmentUploadHandler()
Expand Down Expand Up @@ -333,6 +351,7 @@ def attachments(request, tplname="modoboa_webmail/attachments.html"):

@login_required
@needs_mailbox()
@need_password()
def delattachment(request):
"""Delete an attachment."""
name = request.GET.get("name")
Expand Down Expand Up @@ -539,6 +558,7 @@ def forward(request):

@login_required
@needs_mailbox()
@need_password()
def getmailcontent(request):
mbox = request.GET.get("mbox", None)
mailid = request.GET.get("mailid", None)
Expand All @@ -556,6 +576,7 @@ def getmailcontent(request):

@login_required
@needs_mailbox()
@need_password()
def getmailsource(request):
"""Retrieve message source."""
mbox = request.GET.get("mbox", None)
Expand All @@ -572,7 +593,7 @@ def getmailsource(request):
"source": email.source
})


@need_password()
def viewmail(request):
mbox = request.GET.get("mbox", None)
mailid = request.GET.get("mailid", None)
Expand Down Expand Up @@ -602,6 +623,7 @@ def viewmail(request):

@login_required
@needs_mailbox()
@need_password()
def submailboxes(request):
"""Retrieve the sub mailboxes of a mailbox."""
topmailbox = request.GET.get('topmailbox', '')
Expand All @@ -613,6 +635,7 @@ def submailboxes(request):

@login_required
@needs_mailbox()
@need_password()
def check_unseen_messages(request):
mboxes = request.GET.get("mboxes", None)
if not mboxes:
Expand All @@ -627,6 +650,7 @@ def check_unseen_messages(request):

@login_required
@needs_mailbox()
@need_password()
def index(request):
"""Webmail actions handler
Expand Down Expand Up @@ -704,3 +728,30 @@ def index(request):
del response['status']
http_status = 400
return render_to_json_response(response, status=http_status)


@login_required
@needs_mailbox()
def get_plain_password(request, tplname="modoboa_webmail/ask_password.html"):
"""Get the plain password for the IMAP connection."""
# TODO : REMOVE THIS and use oauth2.

if request.method == "POST":
form = AskPassword(
request.POST,
)
if form.is_valid():
if request.user.check_password(form.cleaned_data["password"]):
request.session["password"] = force_str(encrypt(
form.cleaned_data["password"]
))
return redirect("modoboa_webmail:index")
return render_to_json_response({"form_errors": form.errors}, status=400)
if get_password(request) is not None:
return redirect("modoboa_webmail:index")
form = AskPassword()
context = {
'form': form,
}

return render(request, 'modoboa_webmail/ask_password.html', context)

0 comments on commit 23f91d0

Please sign in to comment.