diff --git a/.gitignore b/.gitignore index 4c2d777..a34ae36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /venv __pycache__ -/data/doc +/data diff --git a/src/api/api_document.py b/src/api/api_document.py index 50db2d5..81d8fc3 100644 --- a/src/api/api_document.py +++ b/src/api/api_document.py @@ -12,8 +12,12 @@ bp = Blueprint('api_document', __name__, url_prefix='/api/doc') def build(): doc_name = get_arg('doc') branch = get_arg('branch', 'master') + apikey = get_arg('apikey') doc = Document(doc_name, branch) + if doc.get_api_key() != apikey: + raise Exception("Invalid API key") + output = "" output += "\n# Pulling source...\n" output += doc.pull() diff --git a/src/app.py b/src/app.py index 1e98859..edcd24b 100644 --- a/src/app.py +++ b/src/app.py @@ -1,13 +1,28 @@ import os +import random +import string from flask import Flask import data.document +import app_globals def create_app(): app = Flask(__name__) src_path = os.path.dirname(os.path.realpath(__file__)) data.document.set_document_root(os.path.realpath(src_path+'/../data/doc')) + app_globals.data_root_dir = os.path.realpath(src_path+'/../data') + + secret_key_path = app_globals.data_root_dir + '/flask-secret-key' + if not os.path.isfile(secret_key_path): + new_secret_key = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(20)) + with open(secret_key_path, 'wb') as f: + f.write(new_secret_key.encode()) + with open(secret_key_path, 'rb') as f: + secret_key = f.read().decode().replace('\n', '').replace('\r', '') + if len(secret_key) < 20: + raise Exception("Internal error: insecure secret key") + app.secret_key = secret_key from api import api_document app.register_blueprint(api_document.bp) diff --git a/src/app_globals.py b/src/app_globals.py new file mode 100644 index 0000000..3351ceb --- /dev/null +++ b/src/app_globals.py @@ -0,0 +1 @@ +admin_password = None diff --git a/src/data/document.py b/src/data/document.py index b57ca54..16ee9c8 100644 --- a/src/data/document.py +++ b/src/data/document.py @@ -1,4 +1,5 @@ import os +import uuid from types import SimpleNamespace from web_utils.run import run import shutil @@ -62,6 +63,10 @@ class Document: def get_url(self): return "/doc/" + sanitize_name(self.doc_name)+'/'+sanitize_name(self.branch) + "/index.html" + def get_api_key(self): + with open(self.doc_path + "/apikey") as f: + return f.read().replace('\n', '') + @staticmethod def make_doc_path(doc_name, branch): doc_path = os.path.realpath(get_document_root()+'/'+sanitize_name(doc_name)+'/'+sanitize_name(branch)) @@ -82,11 +87,15 @@ class Document: # we have potentially serious security issues related to cloning anything. For example cloning from SSH may use a pre-configured server identity, etc. if not repo.startswith("https://"): raise Exception("Only HTTPS repositories are allowed in current implementation") + + # Generate an API key + apikey = str(uuid.uuid4()) target_dir = doc_path + "/repo" cmd = "" cmd += "mkdir -p \"" + target_dir + "\"\n" + cmd += "echo \""+apikey+"\" > \"" + doc_path + "/apikey\"\n" cmd += "cd \"" + target_dir + "\"\n" cmd += "git init \"--initial-branch=" + branch + "\"\n" cmd += "git remote add -f origin \"" + repo + "\"\n" diff --git a/src/templates/admin/document/manage.html b/src/templates/admin/document/manage.html index b29df97..483caf1 100644 --- a/src/templates/admin/document/manage.html +++ b/src/templates/admin/document/manage.html @@ -4,8 +4,9 @@ {% block content %}

Gestion de {{ doc.doc_name }} / {{ doc.branch }}

+

URL permettant de déclencher la compilation : {{ url_for('api_document.build', doc = doc.doc_name, branch = doc.branch, apikey = doc.get_api_key(), _external = True) }}

Consulter
- Compiler
+ Compiler

Supprimer {% endblock %} diff --git a/src/templates/admin/login.html b/src/templates/admin/login.html new file mode 100644 index 0000000..c9675ad --- /dev/null +++ b/src/templates/admin/login.html @@ -0,0 +1,16 @@ + + + + + + Documentation (admin) + + + + +
+
+ + +
+ diff --git a/src/web/admin/admin.py b/src/web/admin/admin.py index a6436ff..3ce2a1e 100644 --- a/src/web/admin/admin.py +++ b/src/web/admin/admin.py @@ -1,9 +1,49 @@ -from flask import Blueprint, render_template +import os +import random +import string +from flask import Blueprint, render_template, session, redirect, url_for, request from data.document import Document +import app_globals bp = Blueprint('admin', __name__, url_prefix='/admin') +def get_admin_password(): + password_path = app_globals.data_root_dir + '/admin-password' + if not os.path.isfile(password_path): + new_password = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12)) + with open(password_path, 'wb') as f: + f.write(new_password.encode()) + with open(password_path, 'rb') as f: + result = f.read().decode().replace('\n', '').replace('\r', '') + if len(result) < 12: + raise Exception("Internal error: insecure password") + return result + +@bp.before_app_request +def authenticate(): + print(request.path) + if request.path == '/admin/login' or request.path.startswith('/doc/') or request.path == '/api/doc/build': + return + + authenticated = session.get('authenticated') + if not authenticated: + return redirect(url_for('admin.login'), code=302) + @bp.route('/') def index(): return render_template("admin/index.html", documents=Document.list()) + +@bp.route('/login', methods=['GET', 'POST']) +def login(): + correct_password = get_admin_password() + if request.method == 'POST': + password = request.form.get('password') + if password == correct_password: + session.clear() + session['authenticated'] = True + return redirect(url_for('admin.index'), code=302) + else: + raise Exception("Incorrect password") + else: + return render_template("admin/login.html") \ No newline at end of file diff --git a/src/web/admin/admin_document.py b/src/web/admin/admin_document.py index d45f83c..f59bab5 100644 --- a/src/web/admin/admin_document.py +++ b/src/web/admin/admin_document.py @@ -1,5 +1,5 @@ import os -from flask import Blueprint, render_template, redirect, url_for, request +from flask import Blueprint, render_template, redirect, url_for, request, session from web_utils.get_arg import get_arg