majuna/app/portal/onion.py
Iain Learmonth f6452cb4fa lint: various non-semantic fixes
bumping fail-under for pylint to 9.72
2022-06-23 13:42:45 +01:00

110 lines
4.6 KiB
Python

from datetime import datetime
from typing import Optional
from flask import flash, redirect, url_for, render_template, Response, Blueprint
from flask.typing import ResponseReturnValue
from flask_wtf import FlaskForm
from sqlalchemy import exc
from wtforms import StringField, SelectField, SubmitField
from wtforms.validators import DataRequired, Length
from app.extensions import db
from app.models.base import Group
from app.models.onions import Onion
from app.portal.util import response_404, view_lifecycle
bp = Blueprint("onion", __name__)
class NewOnionForm(FlaskForm): # type: ignore
domain_name = StringField('Domain Name', validators=[DataRequired()])
onion_name = StringField('Onion Name', validators=[DataRequired(), Length(min=56, max=56)],
description="Onion service hostname, excluding the .onion suffix")
description = StringField('Description', validators=[DataRequired()])
group = SelectField('Group', validators=[DataRequired()])
submit = SubmitField('Save Changes')
class EditOnionForm(FlaskForm): # type: ignore
description = StringField('Description', validators=[DataRequired()])
group = SelectField('Group', validators=[DataRequired()])
submit = SubmitField('Save Changes')
@bp.route("/new", methods=['GET', 'POST'])
@bp.route("/new/<group_id>", methods=['GET', 'POST'])
def onion_new(group_id: Optional[int] = None) -> ResponseReturnValue:
form = NewOnionForm()
form.group.choices = [(x.id, x.group_name) for x in Group.query.all()]
if form.validate_on_submit():
onion = Onion()
onion.group_id = form.group.data
onion.domain_name = form.domain_name.data
onion.onion_name = form.onion_name.data
onion.description = form.description.data
onion.created = datetime.utcnow()
onion.updated = datetime.utcnow()
try:
db.session.add(onion)
db.session.commit()
flash(f"Created new onion {onion.onion_name}.", "success")
return redirect(url_for("portal.onion.onion_edit", onion_id=onion.id))
except exc.SQLAlchemyError:
flash("Failed to create new onion.", "danger")
return redirect(url_for("portal.onion.onion_list"))
if group_id:
form.group.data = group_id
return render_template("new.html.j2", section="onion", form=form)
@bp.route('/edit/<onion_id>', methods=['GET', 'POST'])
def onion_edit(onion_id: int) -> ResponseReturnValue:
onion: Optional[Onion] = Onion.query.filter(Onion.id == onion_id).first()
if onion is None:
return Response(render_template("error.html.j2",
section="onion",
header="404 Onion Not Found",
message="The requested onion service could not be found."),
status=404)
form = EditOnionForm(group=onion.group_id,
description=onion.description)
form.group.choices = [(x.id, x.group_name) for x in Group.query.all()]
if form.validate_on_submit():
onion.group_id = form.group.data
onion.description = form.description.data
onion.updated = datetime.utcnow()
try:
db.session.commit()
flash("Saved changes to group.", "success")
except exc.SQLAlchemyError:
flash("An error occurred saving the changes to the group.", "danger")
return render_template("onion.html.j2",
section="onion",
onion=onion, form=form)
@bp.route("/list")
def onion_list() -> ResponseReturnValue:
onions = Onion.query.order_by(Onion.domain_name).all()
return render_template("list.html.j2",
section="onion",
title="Onion Services",
item="onion service",
new_link=url_for("portal.onion.onion_new"),
items=onions)
@bp.route("/destroy/<onion_id>", methods=['GET', 'POST'])
def onion_destroy(onion_id: int) -> ResponseReturnValue:
onion = Onion.query.filter(Onion.id == onion_id, Onion.destroyed.is_(None)).first()
if onion is None:
return response_404("The requested onion service could not be found.")
return view_lifecycle(
header=f"Destroy onion service {onion.onion_name}",
message=onion.description,
success_message="You will need to manually remove this from the EOTK configuration.",
success_view="portal.onion.onion_list",
section="onion",
resource=onion,
action="destroy"
)