From 2d158f8f771ae1a544ea9b111054cf0c8e4206f6 Mon Sep 17 00:00:00 2001 From: David Rotermund Date: Wed, 17 Jul 2024 11:59:31 +0200 Subject: [PATCH] Add files via upload --- container_to_container/Dockerfile | 26 ++++++++ container_to_container/README.md | 1 + container_to_container/compose.yaml | 20 +++++++ container_to_container/down.sh | 2 + container_to_container/exec.sh | 1 + container_to_container/logs.sh | 2 + container_to_container/make_image.sh | 1 + .../tools/allowed_domains.json | 5 ++ .../tools/blocked_users.json | 6 ++ container_to_container/tools/check_invites.py | 13 ++++ container_to_container/tools/check_user.py | 13 ++++ .../tools/create_account.py | 14 +++++ container_to_container/tools/main.py | 53 +++++++++++++++++ .../tools/process_emails.py | 56 ++++++++++++++++++ container_to_container/tools/run.sh | 1 + container_to_container/tools/secret_key.json | 4 ++ container_to_container/tools/static/logo.png | Bin 0 -> 6126 bytes .../tools/templates/post.html | 49 +++++++++++++++ container_to_container/tools/wsgi.py | 4 ++ container_to_container/up.sh | 2 + 20 files changed, 273 insertions(+) create mode 100644 container_to_container/Dockerfile create mode 100644 container_to_container/compose.yaml create mode 100644 container_to_container/down.sh create mode 100644 container_to_container/exec.sh create mode 100644 container_to_container/logs.sh create mode 100644 container_to_container/make_image.sh create mode 100644 container_to_container/tools/allowed_domains.json create mode 100644 container_to_container/tools/blocked_users.json create mode 100644 container_to_container/tools/check_invites.py create mode 100644 container_to_container/tools/check_user.py create mode 100644 container_to_container/tools/create_account.py create mode 100644 container_to_container/tools/main.py create mode 100644 container_to_container/tools/process_emails.py create mode 100644 container_to_container/tools/run.sh create mode 100644 container_to_container/tools/secret_key.json create mode 100644 container_to_container/tools/static/logo.png create mode 100644 container_to_container/tools/templates/post.html create mode 100644 container_to_container/tools/wsgi.py create mode 100644 container_to_container/up.sh diff --git a/container_to_container/Dockerfile b/container_to_container/Dockerfile new file mode 100644 index 0000000..e664ebb --- /dev/null +++ b/container_to_container/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.12.4 + +RUN apt-get update +RUN apt -y install mc +RUN apt -y install docker.io +RUN pip install pymongo +RUN pip install email_validator +RUN pip install flask +RUN pip install gunicorn +RUN pip install requests +RUN pip install BeautifulSoup4 +RUN apt -y install bash +RUN pip install --upgrade pip +RUN pip install flask_wtf +RUN pip install wtforms +RUN pip install flask_recaptcha +RUN pip install Markup +RUN pip install captcha Pillow + +COPY tools . + +EXPOSE 80 + +ENTRYPOINT ["/bin/bash", "-c", "gunicorn wsgi:app --bind 0.0.0.0:80"] + + diff --git a/container_to_container/README.md b/container_to_container/README.md index 8b13789..328336c 100644 --- a/container_to_container/README.md +++ b/container_to_container/README.md @@ -1 +1,2 @@ +Set a secret key in tools/secret_key.json. Then build the docker image with make_image.sh. diff --git a/container_to_container/compose.yaml b/container_to_container/compose.yaml new file mode 100644 index 0000000..8c95770 --- /dev/null +++ b/container_to_container/compose.yaml @@ -0,0 +1,20 @@ +services: + overleafregister: + image: "overleafregister_image" + container_name: overleafregister + hostname: overleafregister + restart: always + + networks: + - overleaf-network + + volumes: + - overleaf_register:/data + - /var/run/docker.sock:/var/run/docker.sock + +volumes: + overleaf_register: + +networks: + overleaf-network: + external: true diff --git a/container_to_container/down.sh b/container_to_container/down.sh new file mode 100644 index 0000000..c864209 --- /dev/null +++ b/container_to_container/down.sh @@ -0,0 +1,2 @@ +docker compose down + diff --git a/container_to_container/exec.sh b/container_to_container/exec.sh new file mode 100644 index 0000000..a208d2e --- /dev/null +++ b/container_to_container/exec.sh @@ -0,0 +1 @@ +docker exec -it overleafregister bash diff --git a/container_to_container/logs.sh b/container_to_container/logs.sh new file mode 100644 index 0000000..5fd46e9 --- /dev/null +++ b/container_to_container/logs.sh @@ -0,0 +1,2 @@ +docker compose logs -f + diff --git a/container_to_container/make_image.sh b/container_to_container/make_image.sh new file mode 100644 index 0000000..f01fade --- /dev/null +++ b/container_to_container/make_image.sh @@ -0,0 +1 @@ +docker build --network host -t overleafregister_image . diff --git a/container_to_container/tools/allowed_domains.json b/container_to_container/tools/allowed_domains.json new file mode 100644 index 0000000..b6acf6f --- /dev/null +++ b/container_to_container/tools/allowed_domains.json @@ -0,0 +1,5 @@ +{ + "allowed_domains": [ + "uni-bremen.de" + ] +} diff --git a/container_to_container/tools/blocked_users.json b/container_to_container/tools/blocked_users.json new file mode 100644 index 0000000..60d6660 --- /dev/null +++ b/container_to_container/tools/blocked_users.json @@ -0,0 +1,6 @@ +{ + "blocked_users": [ + "" + ] +} + diff --git a/container_to_container/tools/check_invites.py b/container_to_container/tools/check_invites.py new file mode 100644 index 0000000..fdbaf77 --- /dev/null +++ b/container_to_container/tools/check_invites.py @@ -0,0 +1,13 @@ +import pymongo + +def check_invites(email_to_find: str,container_name:str = "overleafmongo", port: int = 27017) -> bool: + client = pymongo.MongoClient(container_name, port) + db = client.sharelatex + project_invites = db.projectInvites + + search_result = project_invites.find_one({"email": email_to_find}) + if search_result is None: + return False + else: + return True + diff --git a/container_to_container/tools/check_user.py b/container_to_container/tools/check_user.py new file mode 100644 index 0000000..b205393 --- /dev/null +++ b/container_to_container/tools/check_user.py @@ -0,0 +1,13 @@ +import pymongo + +def check_user(email_to_find: str, container_name:str = "overleafmongo", port: int = 27017) -> bool: + client = pymongo.MongoClient(container_name, port) + db = client.sharelatex + users = db.users + + search_result = users.find_one({"email": email_to_find}) + if search_result is None: + return False + else: + return True + diff --git a/container_to_container/tools/create_account.py b/container_to_container/tools/create_account.py new file mode 100644 index 0000000..9717ac8 --- /dev/null +++ b/container_to_container/tools/create_account.py @@ -0,0 +1,14 @@ +import subprocess + +def create_account(email: str = "davrot@uni-bremen.de", container_name: str = "overleafserver"): + + work_string: str = f"cd /overleaf/services/web && node modules/server-ce-scripts/scripts/create-user --email={email}" + result = subprocess.run(["docker", "exec", container_name, "/bin/bash", "-ce", work_string ], capture_output=True, text=True) + + success: bool = False + for i in result.stdout.splitlines(): + if i.startswith(f"Successfully created {email} as a"): + success = True + return success + + diff --git a/container_to_container/tools/main.py b/container_to_container/tools/main.py new file mode 100644 index 0000000..1e244e4 --- /dev/null +++ b/container_to_container/tools/main.py @@ -0,0 +1,53 @@ +import json +from flask import Flask, render_template, request, Response, send_from_directory, session +from io import BytesIO +from captcha.image import ImageCaptcha +import random +import base64 +from process_emails import process_emails + +container_name_mongo:str = "overleafmongo" +port_mongo: int = 27017 +container_name_overleaf: str = "overleafserver" + +app = Flask(__name__) + +with open("secret_key.json", "r") as file: + secret_key: dict = json.load(file) + +assert secret_key is not None +assert secret_key["secret_key"] is not None +app.config['SECRET_KEY'] = secret_key["secret_key"] + + +def generate_captcha(): + image = ImageCaptcha(width=280, height=90) + captcha_text = ''.join(random.choices('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', k=6)) + data = image.generate(captcha_text) + return captcha_text, data + +@app.route("/register", methods=["GET", "POST"]) +def index() -> Response: + + if request.method == "GET": + captcha_text, captcha_image = generate_captcha() + session['captcha'] = captcha_text + captcha_base64 = base64.b64encode(captcha_image.getvalue()).decode('utf-8') + return render_template('post.html', captcha_image=captcha_base64) + + elif request.method == "POST": + post_content = request.form.get('content') + email = request.form.get('email') + user_captcha = request.form.get('captcha') + + if user_captcha and user_captcha.upper() == session.get('captcha'): + if process_emails(mail_address=email,container_name_mongo=container_name_mongo,port_mongo=port_mongo,container_name_overleaf=container_name_overleaf): + return f"A email was sent to {email}. Please click the activation link. Please check your spam folder!

Back to the overleaf site..." + else: + return f"We couldn't register your email {email}." + else: + return f"There was a problem with solving the captcha. Try again. Sorry!" + +@app.route("/register/static/", methods=["GET"]) +def serve_static_files(path) -> Response: + return send_from_directory("static", path) diff --git a/container_to_container/tools/process_emails.py b/container_to_container/tools/process_emails.py new file mode 100644 index 0000000..d6cc6aa --- /dev/null +++ b/container_to_container/tools/process_emails.py @@ -0,0 +1,56 @@ +from email_validator import validate_email # type: ignore +import email_validator +import json + +from check_invites import check_invites +from check_user import check_user +from create_account import create_account + +def process_emails( + mail_address: str, + config_file: str = "allowed_domains.json", + blocked_user_file: str = "blocked_users.json", + container_name_mongo:str = "overleafmongo", + port_mongo: int = 27017, + container_name_overleaf: str = "overleafserver", +) -> bool: + + with open(config_file, "r") as file: + allowed_domains: dict = json.load(file) + + with open(blocked_user_file, "r") as file: + blocked_users: dict = json.load(file) + + if (mail_address == "") or (mail_address is None): + return False + try: + emailinfo = validate_email(mail_address, check_deliverability=False) + mail_address = emailinfo.normalized + except email_validator.exceptions_types.EmailSyntaxError: + return False + except email_validator.exceptions_types.EmailNotValidError: + return False + + for blocked_user in blocked_users["blocked_users"]: + if mail_address == blocked_user: + return False + + is_email_allowed: bool = False + + if check_invites(email_to_find=mail_address,container_name=container_name_mongo, port=port_mongo): + is_email_allowed = True + + if check_user(email_to_find=mail_address,container_name=container_name_mongo, port=port_mongo): + is_email_allowed = True + + if is_email_allowed is False: + domain_found: bool = False + for domain in allowed_domains["allowed_domains"]: + if mail_address.endswith(domain): + domain_found = True + + if domain_found is False: + return False + + return create_account(email=mail_address, container_name=container_name_overleaf) + diff --git a/container_to_container/tools/run.sh b/container_to_container/tools/run.sh new file mode 100644 index 0000000..82afc33 --- /dev/null +++ b/container_to_container/tools/run.sh @@ -0,0 +1 @@ +gunicorn wsgi:app --bind 0.0.0.0:80 diff --git a/container_to_container/tools/secret_key.json b/container_to_container/tools/secret_key.json new file mode 100644 index 0000000..8298976 --- /dev/null +++ b/container_to_container/tools/secret_key.json @@ -0,0 +1,4 @@ +{ + "secret_key": "REDACTED" +} + diff --git a/container_to_container/tools/static/logo.png b/container_to_container/tools/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0a1bef967e4a476e3befa0359072c89513a89e4b GIT binary patch literal 6126 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 V=TXxP+5h-*+g!b9RaAf0{{eVHgM + + + + + Register your overleaf account + + + +

+ Logo +
+ +

Register your overleaf account

+
+
+
+ +
+

+

+ +
+ CAPTCHA +
+ +
+

+ +

+ +

What is this?

+This overleaf server is a side project for me.
+Thus, please don't expect a perfectly hosted system with zero downtime. It runs on a ZfN cloud machine.
+If you want to help me maintain this system, you are more than welcome.
+I will do my best but server maintainance is not one of my core duties.
+If you have questions, send me an email: davrot@uni-bremen.de.
+ +

Who can register?

+You can register if you have a University of Bremen email address ending in uni-bremen.de.
+Or someone has invited you to a project. In the latter case you don't need an email address from Univesity of Bremen. + +

Allgemeines

+Impressum
+Datenschutz
+Notfall + + + diff --git a/container_to_container/tools/wsgi.py b/container_to_container/tools/wsgi.py new file mode 100644 index 0000000..fc5f41e --- /dev/null +++ b/container_to_container/tools/wsgi.py @@ -0,0 +1,4 @@ +from main import app + +if __name__ == "__main__": + app.run(debug=True) diff --git a/container_to_container/up.sh b/container_to_container/up.sh new file mode 100644 index 0000000..a4a5dbb --- /dev/null +++ b/container_to_container/up.sh @@ -0,0 +1,2 @@ +docker compose up -d +