Fix: Add csrf verification to directory updates (#1358)
* Fix: Add csrf verification to directory updates * Update templates/dashboard/directory.html * Added csrf for delete account form * Fix tests * Added CSRF check for settings page * Added csrf to batch import * Added CSRF to alias dashboard and alias transfer * Added csrf to contact manager * Added csrf to mailbox * Added csrf for mailbox detail * Added csrf to domain detail * Lint Co-authored-by: Adrià Casajús <adria.casajus@proton.ch>
This commit is contained in:
parent
2f769b38ad
commit
d324e2fa79
|
@ -25,7 +25,7 @@ from app.errors import (
|
|||
)
|
||||
from app.log import LOG
|
||||
from app.models import Alias, Contact, EmailLog, User
|
||||
from app.utils import sanitize_email
|
||||
from app.utils import sanitize_email, CSRFValidationForm
|
||||
|
||||
|
||||
def email_validator():
|
||||
|
@ -258,8 +258,12 @@ def alias_contact_manager(alias_id):
|
|||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
new_contact_form = NewContactForm()
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "create":
|
||||
if new_contact_form.validate():
|
||||
contact_address = new_contact_form.email.data.strip()
|
||||
|
@ -323,4 +327,5 @@ def alias_contact_manager(alias_id):
|
|||
query=query,
|
||||
nb_contact=nb_contact,
|
||||
can_create_contacts=user_can_create_contacts(current_user),
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
|
|
@ -22,6 +22,7 @@ from app.models import (
|
|||
ClientUser,
|
||||
)
|
||||
from app.models import Mailbox
|
||||
from app.utils import CSRFValidationForm
|
||||
|
||||
|
||||
def transfer(alias, new_user, new_mailboxes: [Mailbox]):
|
||||
|
@ -105,8 +106,12 @@ def alias_transfer_send_route(alias_id):
|
|||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
alias_transfer_url = None
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
# generate a new transfer_token
|
||||
if request.form.get("form-name") == "create":
|
||||
transfer_token = f"{alias.id}.{secrets.token_urlsafe(32)}"
|
||||
|
@ -133,6 +138,7 @@ def alias_transfer_send_route(alias_id):
|
|||
alias_transfer_url=alias_transfer_url,
|
||||
link_active=alias.transfer_token_expiration is not None
|
||||
and alias.transfer_token_expiration > arrow.utcnow(),
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from app.dashboard.base import dashboard_bp
|
|||
from app.db import Session
|
||||
from app.log import LOG
|
||||
from app.models import File, BatchImport, Job
|
||||
from app.utils import random_string
|
||||
from app.utils import random_string, CSRFValidationForm
|
||||
|
||||
|
||||
@dashboard_bp.route("/batch_import", methods=["GET", "POST"])
|
||||
|
@ -29,14 +29,21 @@ def batch_import_route():
|
|||
user_id=current_user.id, processed=False
|
||||
).all()
|
||||
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
redirect(request.url)
|
||||
if len(batch_imports) > 10:
|
||||
flash(
|
||||
"You have too many imports already. Wait until some get cleaned up",
|
||||
"error",
|
||||
)
|
||||
return render_template(
|
||||
"dashboard/batch_import.html", batch_imports=batch_imports
|
||||
"dashboard/batch_import.html",
|
||||
batch_imports=batch_imports,
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
||||
alias_file = request.files["alias-file"]
|
||||
|
@ -66,4 +73,6 @@ def batch_import_route():
|
|||
|
||||
return redirect(url_for("dashboard.batch_import_route"))
|
||||
|
||||
return render_template("dashboard/batch_import.html", batch_imports=batch_imports)
|
||||
return render_template(
|
||||
"dashboard/batch_import.html", batch_imports=batch_imports, csrf_form=csrf_form
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import arrow
|
||||
from flask import flash, redirect, url_for, request, render_template
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
|
||||
from app.config import JOB_DELETE_ACCOUNT
|
||||
from app.dashboard.base import dashboard_bp
|
||||
|
@ -9,11 +10,21 @@ from app.log import LOG
|
|||
from app.models import Subscription, Job
|
||||
|
||||
|
||||
class DeleteDirForm(FlaskForm):
|
||||
pass
|
||||
|
||||
|
||||
@dashboard_bp.route("/delete_account", methods=["GET", "POST"])
|
||||
@login_required
|
||||
@sudo_required
|
||||
def delete_account():
|
||||
delete_form = DeleteDirForm()
|
||||
if request.method == "POST" and request.form.get("form-name") == "delete-account":
|
||||
if not delete_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return render_template(
|
||||
"dashboard/delete_account.html", delete_form=delete_form
|
||||
)
|
||||
sub: Subscription = current_user.get_paddle_subscription()
|
||||
# user who has canceled can also re-subscribe
|
||||
if sub and not sub.cancelled:
|
||||
|
@ -36,6 +47,4 @@ def delete_account():
|
|||
)
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
|
||||
return render_template(
|
||||
"dashboard/delete_account.html",
|
||||
)
|
||||
return render_template("dashboard/delete_account.html", delete_form=delete_form)
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
from flask import render_template, request, redirect, url_for, flash
|
||||
from flask_login import login_required, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, validators
|
||||
from wtforms import (
|
||||
StringField,
|
||||
validators,
|
||||
SelectMultipleField,
|
||||
BooleanField,
|
||||
IntegerField,
|
||||
)
|
||||
|
||||
from app.config import (
|
||||
EMAIL_DOMAIN,
|
||||
|
@ -21,6 +27,22 @@ class NewDirForm(FlaskForm):
|
|||
)
|
||||
|
||||
|
||||
class ToggleDirForm(FlaskForm):
|
||||
directory_id = IntegerField(validators=[validators.DataRequired()])
|
||||
directory_enabled = BooleanField(validators=[])
|
||||
|
||||
|
||||
class UpdateDirForm(FlaskForm):
|
||||
directory_id = IntegerField(validators=[validators.DataRequired()])
|
||||
mailbox_ids = SelectMultipleField(
|
||||
validators=[validators.DataRequired()], validate_choice=False, choices=[]
|
||||
)
|
||||
|
||||
|
||||
class DeleteDirForm(FlaskForm):
|
||||
directory_id = IntegerField(validators=[validators.DataRequired()])
|
||||
|
||||
|
||||
@dashboard_bp.route("/directory", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def directory():
|
||||
|
@ -33,54 +55,68 @@ def directory():
|
|||
mailboxes = current_user.mailboxes()
|
||||
|
||||
new_dir_form = NewDirForm()
|
||||
toggle_dir_form = ToggleDirForm()
|
||||
update_dir_form = UpdateDirForm()
|
||||
update_dir_form.mailbox_ids.choices = [
|
||||
(str(mailbox.id), str(mailbox.id)) for mailbox in mailboxes
|
||||
]
|
||||
delete_dir_form = DeleteDirForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if request.form.get("form-name") == "delete":
|
||||
dir_id = request.form.get("dir-id")
|
||||
dir = Directory.get(dir_id)
|
||||
if not delete_dir_form.validate():
|
||||
flash(f"Invalid request", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
dir_obj = Directory.get(delete_dir_form.directory_id.data)
|
||||
|
||||
if not dir:
|
||||
if not dir_obj:
|
||||
flash("Unknown error. Refresh the page", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
elif dir.user_id != current_user.id:
|
||||
elif dir_obj.user_id != current_user.id:
|
||||
flash("You cannot delete this directory", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
name = dir.name
|
||||
Directory.delete(dir_id)
|
||||
name = dir_obj.name
|
||||
Directory.delete(dir_obj.id)
|
||||
Session.commit()
|
||||
flash(f"Directory {name} has been deleted", "success")
|
||||
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
if request.form.get("form-name") == "toggle-directory":
|
||||
dir_id = request.form.get("dir-id")
|
||||
dir = Directory.get(dir_id)
|
||||
if not toggle_dir_form.validate():
|
||||
flash(f"Invalid request", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
dir_id = toggle_dir_form.directory_id.data
|
||||
dir_obj = Directory.get(dir_id)
|
||||
|
||||
if not dir or dir.user_id != current_user.id:
|
||||
if not dir_obj or dir_obj.user_id != current_user.id:
|
||||
flash("Unknown error. Refresh the page", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
if request.form.get("dir-status") == "on":
|
||||
dir.disabled = False
|
||||
flash(f"On-the-fly is enabled for {dir.name}", "success")
|
||||
if toggle_dir_form.directory_enabled.data:
|
||||
dir_obj.disabled = False
|
||||
flash(f"On-the-fly is enabled for {dir_obj.name}", "success")
|
||||
else:
|
||||
dir.disabled = True
|
||||
flash(f"On-the-fly is disabled for {dir.name}", "warning")
|
||||
dir_obj.disabled = True
|
||||
flash(f"On-the-fly is disabled for {dir_obj.name}", "warning")
|
||||
|
||||
Session.commit()
|
||||
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
elif request.form.get("form-name") == "update":
|
||||
dir_id = request.form.get("dir-id")
|
||||
dir = Directory.get(dir_id)
|
||||
if not update_dir_form.validate():
|
||||
flash(f"Invalid request", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
dir_id = update_dir_form.directory_id.data
|
||||
dir_obj = Directory.get(dir_id)
|
||||
|
||||
if not dir or dir.user_id != current_user.id:
|
||||
if not dir_obj or dir_obj.user_id != current_user.id:
|
||||
flash("Unknown error. Refresh the page", "warning")
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
mailbox_ids = request.form.getlist("mailbox_ids")
|
||||
mailbox_ids = update_dir_form.mailbox_ids.data
|
||||
# check if mailbox is not tempered with
|
||||
mailboxes = []
|
||||
for mailbox_id in mailbox_ids:
|
||||
|
@ -99,14 +135,14 @@ def directory():
|
|||
return redirect(url_for("dashboard.directory"))
|
||||
|
||||
# first remove all existing directory-mailboxes links
|
||||
DirectoryMailbox.filter_by(directory_id=dir.id).delete()
|
||||
DirectoryMailbox.filter_by(directory_id=dir_obj.id).delete()
|
||||
Session.flush()
|
||||
|
||||
for mailbox in mailboxes:
|
||||
DirectoryMailbox.create(directory_id=dir.id, mailbox_id=mailbox.id)
|
||||
DirectoryMailbox.create(directory_id=dir_obj.id, mailbox_id=mailbox.id)
|
||||
|
||||
Session.commit()
|
||||
flash(f"Directory {dir.name} has been updated", "success")
|
||||
flash(f"Directory {dir_obj.name} has been updated", "success")
|
||||
|
||||
return redirect(url_for("dashboard.directory"))
|
||||
elif request.form.get("form-name") == "create":
|
||||
|
@ -181,6 +217,9 @@ def directory():
|
|||
return render_template(
|
||||
"dashboard/directory.html",
|
||||
dirs=dirs,
|
||||
toggle_dir_form=toggle_dir_form,
|
||||
update_dir_form=update_dir_form,
|
||||
delete_dir_form=delete_dir_form,
|
||||
new_dir_form=new_dir_form,
|
||||
mailboxes=mailboxes,
|
||||
EMAIL_DOMAIN=EMAIL_DOMAIN,
|
||||
|
|
|
@ -28,7 +28,7 @@ from app.models import (
|
|||
Job,
|
||||
)
|
||||
from app.regex_utils import regex_match
|
||||
from app.utils import random_string
|
||||
from app.utils import random_string, CSRFValidationForm
|
||||
|
||||
|
||||
@dashboard_bp.route("/domains/<int:custom_domain_id>/dns", methods=["GET", "POST"])
|
||||
|
@ -47,6 +47,7 @@ def domain_detail_dns(custom_domain_id):
|
|||
spf_record = f"v=spf1 include:{EMAIL_DOMAIN} ~all"
|
||||
|
||||
domain_validator = CustomDomainValidation(EMAIL_DOMAIN)
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
dmarc_record = "v=DMARC1; p=quarantine; pct=100; adkim=s; aspf=s"
|
||||
|
||||
|
@ -54,6 +55,9 @@ def domain_detail_dns(custom_domain_id):
|
|||
mx_errors = spf_errors = dkim_errors = dmarc_errors = ownership_errors = []
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "check-ownership":
|
||||
txt_records = get_txt_record(custom_domain.domain)
|
||||
|
||||
|
@ -165,6 +169,7 @@ def domain_detail_dns(custom_domain_id):
|
|||
@dashboard_bp.route("/domains/<int:custom_domain_id>/info", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def domain_detail(custom_domain_id):
|
||||
csrf_form = CSRFValidationForm()
|
||||
custom_domain: CustomDomain = CustomDomain.get(custom_domain_id)
|
||||
mailboxes = current_user.mailboxes()
|
||||
|
||||
|
@ -173,6 +178,9 @@ def domain_detail(custom_domain_id):
|
|||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "switch-catch-all":
|
||||
custom_domain.catch_all = not custom_domain.catch_all
|
||||
Session.commit()
|
||||
|
@ -301,12 +309,16 @@ def domain_detail(custom_domain_id):
|
|||
@dashboard_bp.route("/domains/<int:custom_domain_id>/trash", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def domain_detail_trash(custom_domain_id):
|
||||
csrf_form = CSRFValidationForm()
|
||||
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"))
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "empty-all":
|
||||
DomainDeletedAlias.filter_by(domain_id=custom_domain.id).delete()
|
||||
Session.commit()
|
||||
|
@ -350,6 +362,7 @@ def domain_detail_trash(custom_domain_id):
|
|||
"dashboard/domain_detail/trash.html",
|
||||
domain_deleted_aliases=domain_deleted_aliases,
|
||||
custom_domain=custom_domain,
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from app.models import (
|
|||
EmailLog,
|
||||
Contact,
|
||||
)
|
||||
from app.utils import CSRFValidationForm
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -75,8 +76,12 @@ def index():
|
|||
"highlight_alias_id must be a number, received %s",
|
||||
request.args.get("highlight_alias_id"),
|
||||
)
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "create-custom-email":
|
||||
if current_user.can_create_new_alias():
|
||||
return redirect(url_for("dashboard.custom_alias"))
|
||||
|
@ -204,6 +209,7 @@ def index():
|
|||
sort=sort,
|
||||
filter=alias_filter,
|
||||
stats=stats,
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from app.email_utils import (
|
|||
)
|
||||
from app.log import LOG
|
||||
from app.models import Mailbox, Job
|
||||
from app.utils import CSRFValidationForm
|
||||
|
||||
|
||||
class NewMailboxForm(FlaskForm):
|
||||
|
@ -36,8 +37,12 @@ def mailbox_route():
|
|||
)
|
||||
|
||||
new_mailbox_form = NewMailboxForm()
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if request.form.get("form-name") == "delete":
|
||||
mailbox_id = request.form.get("mailbox-id")
|
||||
mailbox = Mailbox.get(mailbox_id)
|
||||
|
@ -127,6 +132,7 @@ def mailbox_route():
|
|||
"dashboard/mailbox.html",
|
||||
mailboxes=mailboxes,
|
||||
new_mailbox_form=new_mailbox_form,
|
||||
csrf_form=csrf_form,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from app.log import LOG
|
|||
from app.models import Alias, AuthorizedAddress
|
||||
from app.models import Mailbox
|
||||
from app.pgp_utils import PGPException, load_public_key_and_check
|
||||
from app.utils import sanitize_email
|
||||
from app.utils import sanitize_email, CSRFValidationForm
|
||||
|
||||
|
||||
class ChangeEmailForm(FlaskForm):
|
||||
|
@ -35,6 +35,7 @@ def mailbox_detail_route(mailbox_id):
|
|||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
change_email_form = ChangeEmailForm()
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
if mailbox.new_email:
|
||||
pending_email = mailbox.new_email
|
||||
|
@ -42,6 +43,9 @@ def mailbox_detail_route(mailbox_id):
|
|||
pending_email = None
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(request.url)
|
||||
if (
|
||||
request.form.get("form-name") == "update-email"
|
||||
and change_email_form.validate_on_submit()
|
||||
|
|
|
@ -53,7 +53,7 @@ from app.models import (
|
|||
UnsubscribeBehaviourEnum,
|
||||
)
|
||||
from app.proton.utils import get_proton_partner, perform_proton_account_unlink
|
||||
from app.utils import random_string, sanitize_email
|
||||
from app.utils import random_string, sanitize_email, CSRFValidationForm
|
||||
|
||||
|
||||
class SettingForm(FlaskForm):
|
||||
|
@ -104,6 +104,7 @@ def setting():
|
|||
form = SettingForm()
|
||||
promo_form = PromoCodeForm()
|
||||
change_email_form = ChangeEmailForm()
|
||||
csrf_form = CSRFValidationForm()
|
||||
|
||||
email_change = EmailChange.get_by(user_id=current_user.id)
|
||||
if email_change:
|
||||
|
@ -112,6 +113,9 @@ def setting():
|
|||
pending_email = None
|
||||
|
||||
if request.method == "POST":
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
if request.form.get("form-name") == "update-email":
|
||||
if change_email_form.validate():
|
||||
# whether user can proceed with the email update
|
||||
|
@ -395,6 +399,7 @@ def setting():
|
|||
|
||||
return render_template(
|
||||
"dashboard/setting.html",
|
||||
csrf_form=csrf_form,
|
||||
form=form,
|
||||
PlanEnum=PlanEnum,
|
||||
SenderFormatEnum=SenderFormatEnum,
|
||||
|
@ -477,9 +482,14 @@ def cancel_email_change():
|
|||
return redirect(url_for("dashboard.setting"))
|
||||
|
||||
|
||||
@dashboard_bp.route("/unlink_proton_account", methods=["GET", "POST"])
|
||||
@dashboard_bp.route("/unlink_proton_account", methods=["POST"])
|
||||
@login_required
|
||||
def unlink_proton_account():
|
||||
csrf_form = CSRFValidationForm()
|
||||
if not csrf_form.validate():
|
||||
flash("Invalid request", "warning")
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
|
||||
perform_proton_account_unlink(current_user)
|
||||
flash("Your Proton account has been unlinked", "success")
|
||||
return redirect(url_for("dashboard.setting"))
|
||||
|
|
|
@ -6,6 +6,7 @@ import urllib.parse
|
|||
from functools import wraps
|
||||
from typing import List, Optional
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from unidecode import unidecode
|
||||
|
||||
from .config import WORDS_FILE_PATH, ALLOWED_REDIRECT_DOMAINS
|
||||
|
@ -126,3 +127,7 @@ def debug_info(func):
|
|||
return ret
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
class CSRFValidationForm(FlaskForm):
|
||||
pass
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
<div class="col-12 col-lg-6 pt-1">
|
||||
<div class="float-right d-flex">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="search">
|
||||
<input type="search"
|
||||
name="query"
|
||||
|
@ -184,6 +185,7 @@
|
|||
</div>
|
||||
<a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete">
|
||||
<input type="hidden" name="contact-id" value="{{ contact.id }}">
|
||||
<span class="card-link btn btn-link float-right delete-forward-email text-danger">Delete</span>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
This transfer URL is <strong>valid for 24 hours</strong>. If it hasn't been used by then it will be automatically disabled.
|
||||
</p>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="remove">
|
||||
<button class="btn btn-warning mt-2">Remove alias transfer URL</button>
|
||||
<div class="small-text">If you don't want to share this alias anymore, you can remove the share URL.</div>
|
||||
|
@ -34,9 +35,10 @@
|
|||
{% if link_active %}
|
||||
|
||||
<p class="alert alert-info">
|
||||
You have an active transfer link. If you don't want to share this alias any more, please delete the link.
|
||||
You have an active transfer link. If you don't want to share this alias anymore, please delete the link.
|
||||
</p>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="remove">
|
||||
<button class="btn btn-warning mt-2">Remove alias transfer URL</button>
|
||||
</form>
|
||||
|
@ -46,6 +48,7 @@
|
|||
please create the <b>Share URL</b> 👇 and send it to the other person.
|
||||
</p>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="create">
|
||||
<button class="btn btn-primary mt-2">Generate a new alias transfer URL</button>
|
||||
</form>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
download>Download CSV Template</a>
|
||||
<hr />
|
||||
<form method="post" enctype="multipart/form-data" class="mt-4">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input required
|
||||
type="file"
|
||||
name="alias-file"
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="form-name" value="delete-account">
|
||||
{{ delete_form.csrf_token }}
|
||||
<span class="delete-account btn btn-outline-danger">Delete account</span>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -66,11 +66,12 @@
|
|||
<div class="d-flex">
|
||||
{{ dir.name }}
|
||||
<form method="post">
|
||||
{{ toggle_dir_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="toggle-directory">
|
||||
<input type="hidden" name="dir-id" value="{{ dir.id }}">
|
||||
{{ toggle_dir_form.directory_id( type="hidden", value=dir.id) }}
|
||||
<label class="custom-switch cursor" style="padding-left: 1rem" data-toggle="tooltip" {% if dir.disabled %}
|
||||
title="Enable directory on-the-fly alias creation" {% else %} title="Disable directory on-the-fly alias creation" {% endif %}>
|
||||
<input type="checkbox" class="custom-switch-input" name="dir-status" {{ "" if dir.disabled else "checked" }}>
|
||||
{{ toggle_dir_form.directory_enabled( class="custom-switch-input", checked=(not dir.disabled) ) }}
|
||||
<span class="custom-switch-indicator"></span>
|
||||
</label>
|
||||
</form>
|
||||
|
@ -92,8 +93,9 @@
|
|||
<br />
|
||||
{% set dir_mailboxes=dir.mailboxes %}
|
||||
<form method="post" class="mt-2">
|
||||
{{ update_dir_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="update">
|
||||
<input type="hidden" name="dir-id" value="{{ dir.id }}">
|
||||
{{ update_dir_form.directory_id( type="hidden", value=dir.id) }}
|
||||
<select data-width="100%"
|
||||
required
|
||||
class="mailbox-select"
|
||||
|
@ -115,9 +117,9 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<form method="post">
|
||||
{{ delete_dir_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete">
|
||||
<input type="hidden" class="dir-name" value="{{ dir.name }}">
|
||||
<input type="hidden" name="dir-id" value="{{ dir.id }}">
|
||||
{{ delete_dir_form.directory_id( type="hidden", value=dir.id) }}
|
||||
<span class="card-link btn btn-link float-right text-danger delete-dir">Delete</span>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -173,18 +175,20 @@
|
|||
|
||||
<script>
|
||||
$(".delete-dir").on("click", function (e) {
|
||||
let directory = $(this).parent().find(".dir-name").val();
|
||||
let directory_name = $(this).parent().find("#directory_name").val();
|
||||
|
||||
const unsanitizedMessage = `All aliases associated with <b>${directory}</b> directory will also be deleted. ` +
|
||||
const element = document.createElement('div');
|
||||
element.innerText = directory_name;
|
||||
const sanitized_name = element.innerHTML;
|
||||
|
||||
const message = `All aliases associated with <b>${sanitized_name}</b> directory will also be deleted. ` +
|
||||
`As a deleted directory can't be used by someone else, deleting a directory doesn't reset your directory quota. ` +
|
||||
`Your directory quota will be {{ current_user.directory_quota }} after the deletion, ` +
|
||||
" please confirm.";
|
||||
const element = document.createElement('div');
|
||||
element.innerText = unsanitizedMessage;
|
||||
const sanitizedMessage = element.innerHTML;
|
||||
|
||||
|
||||
bootbox.confirm({
|
||||
message: sanitizedMessage,
|
||||
message: message,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes, delete it',
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
data-clipboard-text="{{ custom_domain.get_ownership_dns_txt_value() }}">{{ custom_domain.get_ownership_dns_txt_value() }}</em>
|
||||
</div>
|
||||
<form method="post" action="#ownership-form">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="check-ownership">
|
||||
<button type="submit" class="btn btn-primary">Verify</button>
|
||||
</form>
|
||||
|
@ -107,6 +108,7 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
<form method="post" action="#mx-form">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="check-mx">
|
||||
{% if custom_domain.verified %}
|
||||
|
||||
|
@ -180,6 +182,7 @@
|
|||
</em>
|
||||
</div>
|
||||
<form method="post" action="#spf-form">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="check-spf">
|
||||
{% if custom_domain.spf_verified %}
|
||||
|
||||
|
@ -273,6 +276,7 @@
|
|||
<img src="/static/images/cloudflare-proxy.png" class="w-100">
|
||||
</div>
|
||||
<form method="post" action="#dkim-form">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="check-dkim">
|
||||
{% if custom_domain.dkim_verified %}
|
||||
|
||||
|
@ -367,6 +371,7 @@
|
|||
<br />
|
||||
</div>
|
||||
<form method="post" action="#dmarc-form">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="check-dmarc">
|
||||
{% if custom_domain.dmarc_verified %}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<h3 class="mb-1">Auto create/on the fly alias</h3>
|
||||
<div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="switch-catch-all">
|
||||
<label class="custom-switch cursor mt-2 pl-0" data-toggle="tooltip" {% if custom_domain.catch_all %}
|
||||
title="Disable catch-all" {% else %} title="Enable catch-all" {% endif %}>
|
||||
|
@ -41,6 +42,7 @@
|
|||
</div>
|
||||
{% set domain_mailboxes=custom_domain.mailboxes %}
|
||||
<form method="post" class="mt-2">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="update">
|
||||
<input type="hidden" name="domain-id" value="{{ custom_domain.id }}">
|
||||
<div class="d-flex">
|
||||
|
@ -73,6 +75,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<form method="post" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="set-name">
|
||||
<div class="form-group">
|
||||
<input class="form-control mr-2"
|
||||
|
@ -94,6 +97,7 @@
|
|||
<div>Add a random prefix for this domain when creating a new alias.</div>
|
||||
<div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden"
|
||||
name="form-name"
|
||||
value="switch-random-prefix-generation">
|
||||
|
@ -134,6 +138,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete">
|
||||
<span class="delete-custom-domain btn btn-danger">Delete {{ custom_domain.domain }}</span>
|
||||
</form>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
{% if domain_deleted_aliases | length > 0 %}
|
||||
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="empty-all">
|
||||
<button class="btn btn-outline-danger">Empty Trash</button>
|
||||
<div class="small-text">
|
||||
|
@ -42,6 +43,7 @@
|
|||
<b>{{ deleted_alias.email }}</b> -
|
||||
deleted {{ deleted_alias.created_at | dt }}
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="remove-single">
|
||||
<input type="hidden" name="deleted-alias-id" value="{{ deleted_alias.id }}">
|
||||
<button class="btn btn-sm btn-outline-warning">Remove from trash</button>
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
<div>
|
||||
<div class="btn-group" role="group">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="create-custom-email">
|
||||
<button data-toggle="tooltip"
|
||||
title="Create a custom alias"
|
||||
|
@ -99,6 +100,7 @@
|
|||
</form>
|
||||
<div class="btn-group" role="group">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="create-random-email">
|
||||
<button data-toggle="tooltip"
|
||||
title="Create a totally random alias"
|
||||
|
@ -117,6 +119,7 @@
|
|||
aria-labelledby="btnGroupDrop1">
|
||||
<div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="create-random-email">
|
||||
<input type="hidden"
|
||||
name="generator_scheme"
|
||||
|
@ -126,6 +129,7 @@
|
|||
</div>
|
||||
<div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="create-random-email">
|
||||
<input type="hidden"
|
||||
name="generator_scheme"
|
||||
|
@ -152,6 +156,7 @@
|
|||
<!-- Filter Control -->
|
||||
<div class="col d-flex">
|
||||
<form method="get" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<select name="sort"
|
||||
onchange="this.form.submit()"
|
||||
class="form-control mr-3 shadow">
|
||||
|
@ -493,6 +498,7 @@
|
|||
<i class="ml-0 dropdown-icon fe fe-share-2 text-primary"></i>
|
||||
</a>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete-alias">
|
||||
<input type="hidden" name="alias-id" value="{{ alias.id }}">
|
||||
<input type="hidden" name="alias" class="alias" value="{{ alias.email }}">
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
|
||||
<div class="col">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="set-default">
|
||||
<input type="hidden" class="mailbox" value="{{ mailbox.email }}">
|
||||
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
|
||||
|
@ -97,6 +98,7 @@
|
|||
{% endif %}
|
||||
<div class="col">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete">
|
||||
<input type="hidden" class="mailbox" value="{{ mailbox.email }}">
|
||||
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
{% if mailbox.pgp_finger_print %}
|
||||
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="toggle-pgp">
|
||||
<label class="custom-switch cursor" style="padding-left: 1rem" data-toggle="tooltip" {% if mailbox.disable_pgp %}
|
||||
title="Enable PGP" {% else %} title="Disable PGP" {% endif %}>
|
||||
|
@ -108,6 +109,7 @@
|
|||
<div class="alert alert-danger" role="alert">This feature is only available in premium plan.</div>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<div class="form-group">
|
||||
<label class="form-label">PGP Public Key</label>
|
||||
<textarea name="pgp" {% if not current_user.is_premium() %} disabled {% endif %} class="form-control" rows=10 id="pgp-public-key" placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----">{{ mailbox.pgp_public_key or "" }}</textarea>
|
||||
|
@ -127,6 +129,7 @@
|
|||
<div class="card" {% if not mailbox.pgp_enabled() %}
|
||||
disabled {% endif %}>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="generic-subject">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
|
@ -167,6 +170,7 @@
|
|||
|
||||
<div class="card" id="spf">
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="force-spf">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
|
@ -210,6 +214,7 @@
|
|||
<li>
|
||||
{{ authorized_address.email }}
|
||||
<form method="post" action="#authorized-address" style="display: inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="delete-authorized-address">
|
||||
<input type="hidden"
|
||||
name="authorized-address-id"
|
||||
|
@ -221,6 +226,7 @@
|
|||
</ul>
|
||||
{% endif %}
|
||||
<form method="post" action="#authorized-address" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="add-authorized-address">
|
||||
<input type="email" name="email" size="50" class="form-control">
|
||||
<input type="submit" class="btn btn-primary" value="Add">
|
||||
|
|
|
@ -132,6 +132,7 @@
|
|||
<div class="card-title">Newsletters</div>
|
||||
<div class="mb-3">We will occasionally send you emails with new feature announcements.</div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="notification-preference">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
|
@ -231,11 +232,14 @@
|
|||
Your account is currently linked to the Proton account <b>{{ proton_linked_account }}</b>
|
||||
<br />
|
||||
</div>
|
||||
<a class="btn btn-primary mt-2 proton-button"
|
||||
href="{{ url_for('dashboard.unlink_proton_account') }}">
|
||||
<img class="mr-2" src="/static/images/proton.svg">
|
||||
Unlink account
|
||||
</a>
|
||||
<form method="post"
|
||||
action="{{ url_for("dashboard.unlink_proton_account") }}">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<button class="btn btn-primary mt-2 proton-button">
|
||||
<img class="mr-2" src="/static/images/proton.svg">
|
||||
Unlink account
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="mb-3">
|
||||
You can connect your Proton and SimpleLogin accounts.
|
||||
|
@ -262,6 +266,7 @@
|
|||
<div class="card-title">Password</div>
|
||||
<div class="mb-3">You will receive an email containing instructions on how to change your password.</div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="change-password">
|
||||
<button class="btn btn-outline-primary">Change password</button>
|
||||
</form>
|
||||
|
@ -278,6 +283,7 @@
|
|||
Change the way random aliases are generated by default.
|
||||
</div>
|
||||
<form method="post" action="#random-alias" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="change-alias-generator">
|
||||
<select class="form-control mr-sm-2" name="alias-generator-scheme">
|
||||
<option value="{{ AliasGeneratorEnum.word.value }}"
|
||||
|
@ -299,6 +305,7 @@
|
|||
Select the default domain for aliases.
|
||||
</div>
|
||||
<form method="post" action="#random-alias" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden"
|
||||
name="form-name"
|
||||
value="change-random-alias-default-domain">
|
||||
|
@ -330,6 +337,7 @@
|
|||
Select the default suffix generator for aliases.
|
||||
</div>
|
||||
<form method="post" action="#random-alias-suffix" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="random-alias-suffix">
|
||||
<select class="form-control mr-sm-2" name="random-alias-suffix-generator">
|
||||
<option value="0"
|
||||
|
@ -362,6 +370,7 @@
|
|||
in the original form and needs to <b>transform</b> it to one of the formats below.
|
||||
</div>
|
||||
<form method="post" action="#sender-format">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="change-sender-format">
|
||||
<select class="form-control mr-sm-2" name="sender-format">
|
||||
<option value="{{ SenderFormatEnum.AT.value }}"
|
||||
|
@ -408,6 +417,7 @@
|
|||
<br />
|
||||
</div>
|
||||
<form method="post" action="#reverse-alias-replacement-section">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="replace-ra">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
|
@ -440,6 +450,7 @@
|
|||
Please note that existing reverse-aliases won't change.
|
||||
</div>
|
||||
<form method="post" action="#sender-in-ra">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="sender-in-ra">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="include-sender-ra" name="enable"
|
||||
|
@ -471,6 +482,7 @@
|
|||
<br />
|
||||
</div>
|
||||
<form method="post" action="#expand-alias-info-section">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="expand-alias-info">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
|
@ -504,6 +516,7 @@
|
|||
style="max-width: 40%">
|
||||
</div>
|
||||
<form method="post" action="#include_website_in_one_click_alias">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden"
|
||||
name="form-name"
|
||||
value="include_website_in_one_click_alias">
|
||||
|
@ -535,6 +548,7 @@
|
|||
{# You can disable these "loop" emails by enabling this option.#}
|
||||
{# </div>#}
|
||||
{# <form method="post" action="#ignore-loop-email-section">#}
|
||||
{# {{ csrf_form.csrf_token }} #}
|
||||
{# <input type="hidden" name="form-name" value="ignore-loop-email">#}
|
||||
{# <div class="form-check">#}
|
||||
{# <input type="checkbox" id="ignore-loop-email" name="enable"#}
|
||||
|
@ -574,6 +588,7 @@
|
|||
<form method="post"
|
||||
action="#one-click-unsubscribe-section"
|
||||
class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="one-click-unsubscribe">
|
||||
<select class="form-control mr-sm-2" name="unsubscribe-behaviour">
|
||||
<option value="{{ UnsubscribeBehaviourEnum.PreserveOriginal.name }}" {% if current_user.unsub_behaviour.value == UnsubscribeBehaviourEnum.PreserveOriginal.value %}
|
||||
|
@ -633,6 +648,7 @@
|
|||
<br />
|
||||
</div>
|
||||
<form method="post" action="#blocked-behaviour" class="form-inline">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="change-blocked-behaviour">
|
||||
<select class="form-control mr-sm-2" name="blocked-behaviour">
|
||||
<option value="{{ BlockBehaviourEnum.return_2xx.value }}"
|
||||
|
@ -665,6 +681,7 @@
|
|||
As email headers aren't encrypted, your mailbox service can know the sender address via this header.
|
||||
</div>
|
||||
<form method="post" action="#sender-header">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="sender-header">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
|
@ -709,6 +726,7 @@
|
|||
<div class="d-flex">
|
||||
<div>
|
||||
<form method="post">
|
||||
{{ csrf_form.csrf_token }}
|
||||
<input type="hidden" name="form-name" value="send-full-user-report">
|
||||
<button class="btn btn-outline-info">
|
||||
Request your data
|
||||
|
|
|
@ -2,61 +2,66 @@ from flask import url_for
|
|||
|
||||
from app.config import MAX_NB_DIRECTORY
|
||||
from app.models import Directory
|
||||
from tests.utils import login
|
||||
from tests.utils import login, random_token
|
||||
|
||||
|
||||
def test_create_directory(flask_client):
|
||||
login(flask_client)
|
||||
|
||||
directory_name = random_token()
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.directory"),
|
||||
data={"form-name": "create", "name": "test"},
|
||||
data={"form-name": "create", "name": directory_name},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert f"Directory test is created" in r.data.decode()
|
||||
assert Directory.get_by(name="test") is not None
|
||||
assert f"Directory {directory_name} is created" in r.data.decode()
|
||||
assert Directory.get_by(name=directory_name) is not None
|
||||
|
||||
|
||||
def test_delete_directory(flask_client):
|
||||
"""cannot add domain if user personal email uses this domain"""
|
||||
user = login(flask_client)
|
||||
directory = Directory.create(name="test", user_id=user.id, commit=True)
|
||||
directory_name = random_token()
|
||||
directory = Directory.create(name=directory_name, user_id=user.id, commit=True)
|
||||
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.directory"),
|
||||
data={"form-name": "delete", "dir-id": directory.id},
|
||||
data={"form-name": "delete", "directory_id": directory.id},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert f"Directory test has been deleted" in r.data.decode()
|
||||
assert Directory.get_by(name="test") is None
|
||||
assert f"Directory {directory_name} has been deleted" in r.data.decode()
|
||||
assert Directory.get_by(name=directory_name) is None
|
||||
|
||||
|
||||
def test_create_directory_in_trash(flask_client):
|
||||
user = login(flask_client)
|
||||
directory_name = random_token()
|
||||
|
||||
directory = Directory.create(name="test", user_id=user.id, commit=True)
|
||||
directory = Directory.create(name=directory_name, user_id=user.id, commit=True)
|
||||
|
||||
# delete the directory
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.directory"),
|
||||
data={"form-name": "delete", "dir-id": directory.id},
|
||||
data={"form-name": "delete", "directory_id": directory.id},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert Directory.get_by(name="test") is None
|
||||
assert Directory.get_by(name=directory_name) is None
|
||||
|
||||
# try to recreate the directory
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.directory"),
|
||||
data={"form-name": "create", "name": "test"},
|
||||
data={"form-name": "create", "name": directory_name},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert "test has been used before and cannot be reused" in r.data.decode()
|
||||
assert (
|
||||
f"{directory_name} has been used before and cannot be reused" in r.data.decode()
|
||||
)
|
||||
|
||||
|
||||
def test_create_directory_out_of_quota(flask_client):
|
||||
|
|
Loading…
Reference in a new issue