move domain validation (mx, spf, mx) to domain detail page

This commit is contained in:
Son NK 2019-12-27 23:50:09 +01:00 committed by Son Nguyen Kim
parent 34faafe25e
commit 8dcb8dd249
5 changed files with 329 additions and 147 deletions

View File

@ -10,4 +10,5 @@ from .views import (
custom_domain,
alias_contact_manager,
mfa_setup,
domain_detail,
)

View File

@ -24,100 +24,7 @@
<span class="font-weight-bold">{{ custom_domain.nb_alias() }}</span> aliases.
</h6>
{% if not custom_domain.verified %}
<hr>
<div class="mb-3">Please follow the following steps to set up your domain: </div>
<div class="row">
<div class="col-1">
<span class="badge badge-primary badge-pill">1</span>
</div>
<div class="col-11">
Add the following MX DNS record to your domain
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
<div class="ml-2 mb-3 p-3" style="background-color: #eee">
Domain: <em>{{ custom_domain.domain }}</em> <br>
Priority: 10 <br>
Target: <em>{{ email_server }}</em> <br>
</div>
{% endfor %}
Or if you edit your DNS record in text format, use the following code: <br>
<pre class="ml-3">{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}{{ custom_domain.domain }} IN MX {{ priority }} {{ email_server }}<br>{% endfor %}</pre>
</div>
</div>
<div class="row">
<div class="col-1"><span class="badge badge-primary badge-pill">2</span></div>
<div class="col-11">
<span class="font-weight-bold">[Optional]</span>
Setup <a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework" target="_blank">
SPF <i class="fe fe-external-link"></i></a> record.
This can avoid emails forwarded to your personal inbox classified as spam. <br>
Please note that some email providers can still classify these forwards as spam, in this case
do not hesitate to create rules to avoid these emails mistakenly gone into spam.
You can find how to whitelist a domain on
<a href="https://www.simplelogin.io/help" target="_blank">Whitelist domain<i class="fe fe-external-link"></i></a><br>
Please add the following TXT DNS record to your domain:
<div class="ml-3 mb-2 p-3" style="background-color: #eee">
Domain: <em>{{ custom_domain.domain }}</em> <br>
Value:
<em>
v=spf1
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
include:{{ email_server[:-1] }}
{% endfor %} -all
</em>
</div>
Or if you edit your DNS record in text format, use the following code: <br>
<pre class="ml-3">{{ custom_domain.domain }} IN TXT "v=spf1 {% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}include:{{ email_server[:-1] }} {% endfor %}-all"</pre>
</div>
</div>
<div class="row">
<div class="col-1">
<span class="badge badge-primary badge-pill mr-2">3</span>
</div>
<div class="col-11">
Verify 👇🏽
<form method="post">
<input type="hidden" name="form-name" value="check-domain">
<input type="hidden" name="custom-domain-id" value="{{ custom_domain.id }}">
<button type="submit" class="btn btn-primary">Verify</button>
</form>
{% if custom_domain.id in errors %}
<div class="text-danger">
{{ errors.get(custom_domain.id) }}
</div>
{% endif %}
As the change could take up to 24 hours, do not hesitate to come back to this page and verify again.
</div>
</div>
{% endif %}
</div>
<div class="card-footer">
<div class="row">
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="delete">
<input type="hidden" name="custom-domain-id" value="{{ custom_domain.id }}">
<span class="card-link btn btn-link float-right delete-custom-domain">
Delete
</span>
</form>
</div>
</div>
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=custom_domain.id) }}">Details ➡</a>
</div>
</div>
{% endfor %}

View File

@ -0,0 +1,203 @@
{% extends 'default.html' %}
{% block title %}
{{ custom_domain.domain }}
{% endblock %}
{% block head %}
{% endblock %}
{% block default_content %}
<div class="bg-white p-4" style="max-width: 60rem; margin: auto">
<h1 class="h3"> {{ custom_domain.domain }} </h1>
<div class="">Please follow the steps below to set up your domain.</div>
<div class="small-text mb-5">
DNS changes could take up to 24 hours to propagate. In practice, it's a lot faster though (~1
minute or in our experience).
</div>
<div>
<div class="font-weight-bold">1. MX record
{% if custom_domain.verified %} ✅ {% else %} 🚫{% endif %}
</div>
<div class="mb-2">Add the following MX DNS record to your domain</div>
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
<div class="mb-3 p-3" style="background-color: #eee">
Domain: <em>{{ custom_domain.domain }}</em> <br>
Priority: 10 <br>
Target: <em>{{ email_server }}</em> <br>
</div>
{% endfor %}
<form method="post">
<input type="hidden" name="form-name" value="check-mx">
{% if custom_domain.verified %}
<button type="submit" class="btn btn-outline-primary">
Re-verify
</button>
{% else %}
<button type="submit" class="btn btn-primary">
Verify
</button>
{% endif %}
</form>
{% if not mx_ok %}
<div class="text-danger mt-4">
Your DNS is not correctly set. The MX record we obtain is:
<div class="mb-3 p-3" style="background-color: #eee">
{% for r in mx_errors %}
{{ r }} <br>
{% endfor %}
</div>
{% if custom_domain.verified %}
Please make sure to fix this ASAP - your aliases might not work properly.
{% endif %}
</div>
{% endif %}
</div>
<hr>
<div>
<div class="font-weight-bold">2. SPF (Optional)
{% if custom_domain.spf_verified %} ✅ {% else %} 🚫{% endif %}
</div>
<div>
SPF <a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework" target="_blank">(Wikipedia↗)</a> is an email
authentication method
designed to detect forging sender addresses during the delivery of the email. <br>
Setting up SPF is highly recommended to reduce the chance your emails ending up in the recipient's Spam folder.
</div>
<div class="mb-2">Add the following TXT DNS record to your domain</div>
<div class="mb-2 p-3" style="background-color: #eee">
Domain: <em>{{ custom_domain.domain }}</em> <br>
Value:
<em>
{{ spf_record }}
</em>
</div>
<form method="post">
<input type="hidden" name="form-name" value="check-spf">
{% if custom_domain.spf_verified %}
<button type="submit" class="btn btn-outline-primary">
Re-verify
</button>
{% else %}
<button type="submit" class="btn btn-primary">
Verify
</button>
{% endif %}
</form>
{% if not spf_ok %}
<div class="text-danger mt-4">
Your DNS is not correctly set. The TXT record we obtain is:
<div class="mb-3 p-3" style="background-color: #eee">
{% for r in spf_errors %}
{{ r }} <br>
{% endfor %}
</div>
{% if custom_domain.spf_verified %}
Without SPF setup, emails you sent from your alias might end up in Spam/Junk folder.
{% endif %}
</div>
{% endif %}
</div>
<hr>
<div>
<div class="font-weight-bold">3. DKIM (Optional)
{% if custom_domain.dkim_verified %} ✅ {% else %} 🚫{% endif %}
</div>
<div>
DKIM <a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail" target="_blank">(Wikipedia↗)</a> is an
email
authentication method
designed to avoid email spoofing. <br>
Setting up DKIM is highly recommended to reduce the chance your emails ending up in the recipient's Spam folder.
</div>
<div class="mb-2">Add the following TXT DNS record to your domain</div>
<div class="mb-2 p-3" style="background-color: #eee">
Domain: <em>dkim._domainkey.{{ custom_domain.domain }}</em> <br>
Value:
<em style="overflow-wrap: break-word">
{{ dkim_record }}
</em>
</div>
<form method="post">
<input type="hidden" name="form-name" value="check-dkim">
{% if custom_domain.dkim_verified %}
<button type="submit" class="btn btn-outline-primary">
Re-verify
</button>
{% else %}
<button type="submit" class="btn btn-primary">
Verify
</button>
{% endif %}
</form>
{% if not dkim_ok %}
<div class="text-danger mt-4">
Your DNS is not correctly set.
{% if dkim_errors %}
The TXT record we obtain for
<em>dkim._domainkey.{{ custom_domain.domain }}</em> is:
<div class="mb-3 p-3" style="background-color: #eee">
{% for r in dkim_errors %}
{{ r }} <br>
{% endfor %}
</div>
{% endif %}
{% if custom_domain.dkim_verified %}
Without DKIM setup, emails you sent from your alias might end up in Spam/Junk folder.
{% endif %}
</div>
{% endif %}
</div>
<hr>
<h3 class="mb-0">Delete Domain</h3>
<div class="small-text mb-3">Please note that this operation is irreversible.
All aliases associated with this domain will be also deleted
</div>
<form method="post">
<input type="hidden" name="form-name" value="delete">
<span class="delete-custom-domain btn btn-outline-danger">Delete domain</span>
</form>
</div>
{% endblock %}
{% block script %}
<script>
$(".delete-custom-domain").on("click", function (e) {
notie.confirm({
text: "All aliases associated with <b>{{ custom_domain.domain }}</b> will be also deleted, " +
" please confirm.",
cancelCallback: () => {
// nothing to do
},
submitCallback: () => {
$(this).closest("form").submit();
}
});
});
</script>
{% endblock %}

View File

@ -3,9 +3,8 @@ from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, validators
from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_SERVERS
from app.config import EMAIL_SERVERS_WITH_PRIORITY
from app.dashboard.base import dashboard_bp
from app.dns_utils import get_mx_domains, get_spf_domain
from app.extensions import db
from app.models import CustomDomain
@ -30,25 +29,7 @@ def custom_domain():
errors = {}
if request.method == "POST":
if request.form.get("form-name") == "delete":
custom_domain_id = request.form.get("custom-domain-id")
custom_domain = CustomDomain.get(custom_domain_id)
if not custom_domain:
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.custom_domain"))
elif custom_domain.user_id != current_user.id:
flash("You cannot delete this domain", "warning")
return redirect(url_for("dashboard.custom_domain"))
name = custom_domain.domain
CustomDomain.delete(custom_domain_id)
db.session.commit()
flash(f"Domain {name} has been deleted successfully", "success")
return redirect(url_for("dashboard.custom_domain"))
elif request.form.get("form-name") == "create":
if request.form.get("form-name") == "create":
if new_custom_domain_form.validate():
new_custom_domain = CustomDomain.create(
domain=new_custom_domain_form.domain.data, user_id=current_user.id
@ -60,39 +41,11 @@ def custom_domain():
"success",
)
return redirect(url_for("dashboard.custom_domain"))
elif request.form.get("form-name") == "check-domain":
custom_domain_id = request.form.get("custom-domain-id")
custom_domain = CustomDomain.get(custom_domain_id)
if not custom_domain:
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.custom_domain"))
elif custom_domain.user_id != current_user.id:
flash("You cannot delete this domain", "warning")
return redirect(url_for("dashboard.custom_domain"))
else:
spf_domains = get_spf_domain(custom_domain.domain)
for email_server in EMAIL_SERVERS:
email_server = email_server[:-1] # remove the trailing .
if email_server not in spf_domains:
flash(
f"{email_server} is not included in your SPF record.",
"warning",
)
mx_domains = get_mx_domains(custom_domain.domain)
if mx_domains != EMAIL_SERVERS:
errors[
custom_domain.id
] = f"""Your DNS is not correctly set. The MX record we obtain is: {",".join(mx_domains)}"""
else:
flash(
"Your domain is verified. Now it can be used to create custom alias",
"success",
return redirect(
url_for(
"dashboard.domain_detail", custom_domain_id=new_custom_domain.id
)
custom_domain.verified = True
db.session.commit()
)
return render_template(
"dashboard/custom_domain.html",

View File

@ -0,0 +1,118 @@
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_SERVERS, DKIM_DNS_VALUE
from app.dashboard.base import dashboard_bp
from app.dns_utils import (
get_mx_domains,
get_spf_domain,
get_dkim_record,
get_txt_record,
)
from app.extensions import db
from app.models import CustomDomain
@dashboard_bp.route("/domains/<int:custom_domain_id>", methods=["GET", "POST"])
@login_required
def domain_detail(custom_domain_id):
# only premium user can see custom domain
if not current_user.is_premium():
flash("Only premium user can add custom domains", "warning")
return redirect(url_for("dashboard.index"))
custom_domain = CustomDomain.get(custom_domain_id)
if not custom_domain or custom_domain.user_id != current_user.id:
flash("You cannot see this page", "warning")
return redirect(url_for("dashboard.index"))
mx_ok = spf_ok = dkim_ok = True
mx_errors = spf_errors = dkim_errors = []
if request.method == "POST":
if request.form.get("form-name") == "check-mx":
mx_domains = get_mx_domains(custom_domain.domain)
if mx_domains != EMAIL_SERVERS:
mx_ok = False
mx_errors = get_mx_domains(custom_domain.domain, keep_priority=True)
else:
flash(
"Your domain is verified. Now it can be used to create custom alias",
"success",
)
custom_domain.verified = True
db.session.commit()
return redirect(
url_for(
"dashboard.domain_detail", custom_domain_id=custom_domain.id
)
)
elif request.form.get("form-name") == "check-spf":
spf_domains = get_spf_domain(custom_domain.domain)
for email_server in EMAIL_SERVERS:
email_server = email_server[:-1] # remove the trailing .
if email_server not in spf_domains:
flash(
f"{email_server} is not included in your SPF record.", "warning"
)
spf_ok = False
if spf_ok:
custom_domain.spf_verified = True
db.session.commit()
flash("The SPF is setup correctly", "success")
return redirect(
url_for(
"dashboard.domain_detail", custom_domain_id=custom_domain.id
)
)
else:
spf_errors = get_txt_record(custom_domain.domain)
elif request.form.get("form-name") == "check-dkim":
dkim_record = get_dkim_record(custom_domain.domain)
correct_dkim_record = f"v=DKIM1; k=rsa; p={DKIM_DNS_VALUE}"
if dkim_record == correct_dkim_record:
flash("The DKIM is setup correctly.", "success")
custom_domain.dkim_verified = True
db.session.commit()
return redirect(
url_for(
"dashboard.domain_detail", custom_domain_id=custom_domain.id
)
)
else:
dkim_ok = False
dkim_errors = get_txt_record(f"dkim._domainkey.{custom_domain.domain}")
elif request.form.get("form-name") == "delete":
name = custom_domain.domain
CustomDomain.delete(custom_domain_id)
db.session.commit()
flash(f"Domain {name} has been deleted successfully", "success")
return redirect(url_for("dashboard.custom_domain"))
spf_include_records = []
for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY:
spf_include_records.append(f"include:{email_server[:-1]}")
spf_record = f"v=spf1 {' '.join(spf_include_records)} -all"
dkim_record = f"v=DKIM1; k=rsa; p={DKIM_DNS_VALUE}"
return render_template(
"dashboard/domain_detail.html",
custom_domain=custom_domain,
EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY,
spf_record=spf_record,
dkim_record=dkim_record,
mx_errors=mx_errors,
mx_ok=mx_ok,
spf_errors=spf_errors,
spf_ok=spf_ok,
dkim_errors=dkim_errors,
dkim_ok=dkim_ok,
)