Skip to content

Commit

Permalink
Improve docroot validation, make add/edit consistent
Browse files Browse the repository at this point in the history
  • Loading branch information
rsa33 committed Nov 15, 2024
1 parent 20cb375 commit 56fc85d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 19 deletions.
24 changes: 14 additions & 10 deletions control/webapp/member.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import re
import string

from flask import Blueprint, redirect, render_template, request, url_for
from werkzeug.exceptions import Forbidden, NotFound

Expand All @@ -10,7 +7,10 @@
from srcf.database import Domain

from . import inspect_services, utils
from .utils import create_job_maybe_email_and_redirect, effective_member, parse_domain_name, srcf_db_sess as sess
from .utils import (
create_job_maybe_email_and_redirect, effective_member, parse_domain_name,
validate_domain_docroot, srcf_db_sess as sess,
)


bp = Blueprint("member", __name__)
Expand Down Expand Up @@ -252,6 +252,7 @@ def add_vhost():

domain = request.form.get("domain", "").strip()
root = request.form.get("root", "").strip()

if domain:
parsed = parse_domain_name(domain)
if domain != parsed:
Expand All @@ -270,6 +271,11 @@ def add_vhost():
else:
errors["domain"] = "Please enter a domain or subdomain."

if root:
root, msg = validate_domain_docroot(mem, root)
if msg:
errors["root"] = msg

if request.form.get("edit") or errors:
return render_template("member/add_vhost.html", member=mem, domain=domain, root=root, errors=errors)
elif not request.form.get("confirm"):
Expand Down Expand Up @@ -303,12 +309,10 @@ def change_vhost_docroot(domain):

if request.method == "POST":
root = request.form.get("root", "").strip()
if any([ch in root for ch in string.whitespace + "\\" + "\"" + "\'"]) or ".." in root:
errors["root"] = "This document root is invalid."
try:
domain = parse_domain_name(domain)
except ValueError as e:
errors["domain"] = e.args[0]
if root:
root, msg = validate_domain_docroot(mem, root)
if msg:
errors["root"] = msg

if request.method == "POST" and not errors:
return create_job_maybe_email_and_redirect(
Expand Down
22 changes: 14 additions & 8 deletions control/webapp/society.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
import string

from flask import Blueprint, redirect, render_template, request, url_for
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
Expand All @@ -10,7 +9,10 @@
from srcf.database import Domain

from . import inspect_services, utils
from .utils import create_job_maybe_email_and_redirect, find_mem_society, parse_domain_name, srcf_db_sess as sess
from .utils import (
create_job_maybe_email_and_redirect, find_mem_society, parse_domain_name,
validate_domain_docroot, srcf_db_sess as sess,
)


bp = Blueprint("society", __name__)
Expand Down Expand Up @@ -286,6 +288,7 @@ def add_vhost(society):

domain = request.form.get("domain", "").strip()
root = request.form.get("root", "").strip()

if domain:
parsed = parse_domain_name(domain)
if domain != parsed:
Expand All @@ -304,6 +307,11 @@ def add_vhost(society):
else:
errors["domain"] = "Please enter a domain or subdomain."

if root:
root, msg = validate_domain_docroot(mem, root)
if msg:
errors["root"] = msg

if request.form.get("edit") or errors:
return render_template("society/add_vhost.html", society=soc, member=mem, domain=domain, root=root, errors=errors)
elif not request.form.get("confirm"):
Expand Down Expand Up @@ -337,12 +345,10 @@ def change_vhost_docroot(society, domain):

if request.method == "POST":
root = request.form.get("root", "").strip()
if any([ch in root for ch in string.whitespace + "\\" + "\"" + "\'"]) or ".." in root:
errors["root"] = "This document root is invalid."
try:
domain = parse_domain_name(domain)
except ValueError as e:
errors["domain"] = e.args[0]
if root:
root, msg = validate_domain_docroot(mem, root)
if msg:
errors["root"] = msg

if request.method == "POST" and not errors:
return create_job_maybe_email_and_redirect(
Expand Down
30 changes: 29 additions & 1 deletion control/webapp/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from functools import partial
import os
import string
import sys
import traceback
from urllib.parse import urlparse
Expand All @@ -15,7 +16,7 @@

from srcf.controllib.jobs import CreateSociety, Reactivate, Signup, SocietyJob
from srcf.controllib.utils import email_re, is_admin, ldapsearch, mysql_conn
from srcf.database import JobLog, queries, Session
from srcf.database import Member, JobLog, queries, Session
from srcf.mail import mail_sysadmins
import ucam_webauth
import ucam_webauth.flask_glue
Expand Down Expand Up @@ -91,6 +92,33 @@ def parse_domain_name(domain):
return domain.encode("idna").decode("ascii")


def validate_domain_docroot(owner, path):
if not path:
return path, None
if any(ch in path for ch in string.whitespace + "\\" + '"' + "'"):
return path, "Document roots cannot contain spaces or quotes."
if path.startswith("public_html/"):
path = path.replace("public_html/", "", 1)
if isinstance(owner, Member):
username = owner.crsid
top = "home"
else:
username = owner.society
top = "societies"
base = os.path.join("/public", top, username, "public_html")
target = os.path.abspath(os.path.join(base, path))
if not target.startswith(base):
return path, "Document roots must be inside your public_html directory."
elif base == target:
return "", "We've cleared your document root as it appears to be your public_html directory."
elif not os.path.exists(target):
return path, "This document root doesn't exist, or isn't accessible to the webserver. Create the directory first, then try again."
clean = target[len(base) + 1:]
if clean != path:
return clean, "We've fixed your document root to its canonical version; submit again to confirm."
return path, None


# Template helpers
def sif(variable, val):
""""string if": `val` if `variable` is defined and truthy, else ''"""
Expand Down

0 comments on commit 56fc85d

Please sign in to comment.