mirror of
https://github.com/simple-login/app.git
synced 2024-09-29 21:21:29 +02:00
Handle mailbox change
This commit is contained in:
parent
aa784a0593
commit
731d898774
@ -16,4 +16,5 @@ from .views import (
|
|||||||
directory,
|
directory,
|
||||||
mailbox,
|
mailbox,
|
||||||
deleted_alias,
|
deleted_alias,
|
||||||
|
mailbox_detail,
|
||||||
)
|
)
|
||||||
|
@ -20,13 +20,15 @@
|
|||||||
A <em>mailbox</em> is just another personal email address. When creating a new alias, you could choose the
|
A <em>mailbox</em> is just another personal email address. When creating a new alias, you could choose the
|
||||||
mailbox that <em>owns</em> this alias, i.e: <br>
|
mailbox that <em>owns</em> this alias, i.e: <br>
|
||||||
- all emails sent to this alias will be forwarded to this mailbox <br>
|
- all emails sent to this alias will be forwarded to this mailbox <br>
|
||||||
- from this mailbox, you can reply/send emails from the alias. <br>
|
- from this mailbox, you can reply/send emails from the alias. <br><br>
|
||||||
|
|
||||||
By default, all aliases are owned by your email <b>{{ current_user.email }}</b>. <br><br>
|
{% if current_user.full_mailbox %}
|
||||||
|
When you signed up, a mailbox is automatically created with your email <b>{{ current_user.email }}</b>
|
||||||
|
<br><br>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
The mailbox doesn't have to be your email: it can be your friend's email
|
The mailbox doesn't have to be your email: it can be your friend's email
|
||||||
if you want to create aliases for your buddy. <br>
|
if you want to create aliases for your buddy.
|
||||||
They just need to validate this mailbox when they receive the activation email.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% for mailbox in mailboxes %}
|
{% for mailbox in mailboxes %}
|
||||||
@ -45,8 +47,10 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<h6 class="card-subtitle mb-2 text-muted">
|
<h6 class="card-subtitle mb-2 text-muted">
|
||||||
Created {{ mailbox.created_at | dt }} <br>
|
Created {{ mailbox.created_at | dt }} <br>
|
||||||
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases.
|
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases. <br>
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
|
<a href="{{ url_for('dashboard.mailbox_detail_route', mailbox_id=mailbox.id) }}">Edit ➡</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-footer p-0">
|
<div class="card-footer p-0">
|
||||||
|
58
app/dashboard/templates/dashboard/mailbox_detail.html
Normal file
58
app/dashboard/templates/dashboard/mailbox_detail.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{% extends 'default.html' %}
|
||||||
|
|
||||||
|
{% set active_page = "mailbox" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Mailbox {{ mailbox.email }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block default_content %}
|
||||||
|
|
||||||
|
<div class="col-md-8 offset-md-2 pb-3">
|
||||||
|
<h1 class="h3">{{ mailbox.email }}
|
||||||
|
{% if mailbox.verified %}
|
||||||
|
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Verified">✅</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Not Verified">
|
||||||
|
🚫
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<!-- Change email -->
|
||||||
|
<div class="card">
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="form-name" value="update-email">
|
||||||
|
{{ change_email_form.csrf_token }}
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-title">
|
||||||
|
Change Mailbox Address
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Address</label>
|
||||||
|
|
||||||
|
<!-- Not allow user to change mailbox if there's a pending change -->
|
||||||
|
{{ change_email_form.email(class="form-control", value=mailbox.email, readonly=pending_email != None) }}
|
||||||
|
{{ render_field_errors(change_email_form.email) }}
|
||||||
|
|
||||||
|
{% if pending_email %}
|
||||||
|
<div class="mt-2">
|
||||||
|
<span class="text-danger">Pending change: {{ pending_email }}</span>
|
||||||
|
<a href="{{ url_for('dashboard.cancel_mailbox_change_route', mailbox_id=mailbox.id) }}"
|
||||||
|
class="btn btn-secondary btn-sm">
|
||||||
|
Cancel mailbox change
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary">Change</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<!-- END Change email -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
150
app/dashboard/views/mailbox_detail.py
Normal file
150
app/dashboard/views/mailbox_detail.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
from flask import render_template, request, redirect, url_for, flash
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from itsdangerous import Signer, BadSignature
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.fields.html5 import EmailField
|
||||||
|
|
||||||
|
from app.config import FLASK_SECRET
|
||||||
|
from app.config import URL
|
||||||
|
from app.dashboard.base import dashboard_bp
|
||||||
|
from app.email_utils import can_be_used_as_personal_email, email_already_used
|
||||||
|
from app.email_utils import (
|
||||||
|
send_email,
|
||||||
|
render,
|
||||||
|
)
|
||||||
|
from app.extensions import db
|
||||||
|
from app.log import LOG
|
||||||
|
from app.models import (
|
||||||
|
GenEmail,
|
||||||
|
DeletedAlias,
|
||||||
|
)
|
||||||
|
from app.models import Mailbox
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeEmailForm(FlaskForm):
|
||||||
|
email = EmailField(
|
||||||
|
"email", validators=[validators.DataRequired(), validators.Email()]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dashboard_bp.route("/mailbox/<int:mailbox_id>/", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def mailbox_detail_route(mailbox_id):
|
||||||
|
mailbox = Mailbox.get(mailbox_id)
|
||||||
|
if not mailbox or mailbox.user_id != current_user.id:
|
||||||
|
flash("You cannot see this page", "warning")
|
||||||
|
return redirect(url_for("dashboard.index"))
|
||||||
|
|
||||||
|
change_email_form = ChangeEmailForm()
|
||||||
|
|
||||||
|
if mailbox.new_email:
|
||||||
|
pending_email = mailbox.new_email
|
||||||
|
else:
|
||||||
|
pending_email = None
|
||||||
|
|
||||||
|
if change_email_form.validate_on_submit():
|
||||||
|
new_email = change_email_form.email.data
|
||||||
|
if new_email != mailbox.email and not pending_email:
|
||||||
|
# check if this email is not already used
|
||||||
|
if (
|
||||||
|
email_already_used(new_email)
|
||||||
|
or GenEmail.get_by(email=new_email)
|
||||||
|
or DeletedAlias.get_by(email=new_email)
|
||||||
|
):
|
||||||
|
flash(f"Email {new_email} already used", "error")
|
||||||
|
elif not can_be_used_as_personal_email(new_email):
|
||||||
|
flash(
|
||||||
|
"You cannot use this email address as your mailbox", "error",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mailbox.new_email = new_email
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
s = Signer(FLASK_SECRET)
|
||||||
|
mailbox_id_signed = s.sign(str(mailbox.id)).decode()
|
||||||
|
verification_url = (
|
||||||
|
URL
|
||||||
|
+ "/dashboard/mailbox/confirm_change"
|
||||||
|
+ f"?mailbox_id={mailbox_id_signed}"
|
||||||
|
)
|
||||||
|
|
||||||
|
send_email(
|
||||||
|
new_email,
|
||||||
|
f"Confirm mailbox change on SimpleLogin",
|
||||||
|
render(
|
||||||
|
"transactional/verify-mailbox-change.txt",
|
||||||
|
user=current_user,
|
||||||
|
link=verification_url,
|
||||||
|
mailbox_email=mailbox.email,
|
||||||
|
mailbox_new_email=new_email,
|
||||||
|
),
|
||||||
|
render(
|
||||||
|
"transactional/verify-mailbox-change.html",
|
||||||
|
user=current_user,
|
||||||
|
link=verification_url,
|
||||||
|
mailbox_email=mailbox.email,
|
||||||
|
mailbox_new_email=new_email,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
flash(
|
||||||
|
f"You are going to receive an email to confirm {new_email}.",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_template("dashboard/mailbox_detail.html", **locals(),)
|
||||||
|
|
||||||
|
|
||||||
|
@dashboard_bp.route(
|
||||||
|
"/mailbox/<int:mailbox_id>/cancel_email_change", methods=["GET", "POST"]
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def cancel_mailbox_change_route(mailbox_id):
|
||||||
|
mailbox = Mailbox.get(mailbox_id)
|
||||||
|
if not mailbox or mailbox.user_id != current_user.id:
|
||||||
|
flash("You cannot see this page", "warning")
|
||||||
|
return redirect(url_for("dashboard.index"))
|
||||||
|
|
||||||
|
if mailbox.new_email:
|
||||||
|
mailbox.new_email = None
|
||||||
|
db.session.commit()
|
||||||
|
flash("Your mailbox change is cancelled", "success")
|
||||||
|
return redirect(
|
||||||
|
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
flash("You have no pending mailbox change", "warning")
|
||||||
|
return redirect(
|
||||||
|
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dashboard_bp.route("/mailbox/confirm_change")
|
||||||
|
def mailbox_confirm_change_route():
|
||||||
|
s = Signer(FLASK_SECRET)
|
||||||
|
mailbox_id = request.args.get("mailbox_id")
|
||||||
|
|
||||||
|
try:
|
||||||
|
r_id = int(s.unsign(mailbox_id))
|
||||||
|
except BadSignature:
|
||||||
|
flash("Invalid link", "error")
|
||||||
|
else:
|
||||||
|
mailbox = Mailbox.get(r_id)
|
||||||
|
mailbox.email = mailbox.new_email
|
||||||
|
mailbox.new_email = None
|
||||||
|
|
||||||
|
# mark mailbox as verified if the change request is sent from an unverified mailbox
|
||||||
|
mailbox.verified = True
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
LOG.d("Mailbox change %s is verified", mailbox)
|
||||||
|
flash(
|
||||||
|
f"The {mailbox.email} is updated", "success",
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox.id)
|
||||||
|
)
|
11
templates/emails/transactional/verify-mailbox-change.html
Normal file
11
templates/emails/transactional/verify-mailbox-change.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{{ render_text("Hi " + user.name) }}
|
||||||
|
{{ render_text("You recently requested to change mailbox <b>"+ mailbox_email +"</b> to <b>" + mailbox_new_email + "</b>.") }}
|
||||||
|
{{ render_text("To confirm, please click on the button below.") }}
|
||||||
|
{{ render_button("Confirm mailbox change", link) }}
|
||||||
|
{{ render_text('Thanks, <br />SimpleLogin Team.') }}
|
||||||
|
{{ raw_url(link) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
10
templates/emails/transactional/verify-mailbox-change.txt
Normal file
10
templates/emails/transactional/verify-mailbox-change.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Hi {{user.name}}
|
||||||
|
|
||||||
|
You recently requested to change mailbox {{mailbox_email}} to {{mailbox_new_email}}
|
||||||
|
|
||||||
|
To confirm, please click on this link:
|
||||||
|
|
||||||
|
{{link}}
|
||||||
|
|
||||||
|
Regards,
|
||||||
|
Son - SimpleLogin founder.
|
Loading…
Reference in New Issue
Block a user