implemented authentication to access admin pages
This commit is contained in:
parent
df476d9865
commit
3a392135d1
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
/venv
|
/venv
|
||||||
__pycache__
|
__pycache__
|
||||||
/data/doc
|
/data
|
||||||
|
@ -12,8 +12,12 @@ bp = Blueprint('api_document', __name__, url_prefix='/api/doc')
|
|||||||
def build():
|
def build():
|
||||||
doc_name = get_arg('doc')
|
doc_name = get_arg('doc')
|
||||||
branch = get_arg('branch', 'master')
|
branch = get_arg('branch', 'master')
|
||||||
|
apikey = get_arg('apikey')
|
||||||
doc = Document(doc_name, branch)
|
doc = Document(doc_name, branch)
|
||||||
|
|
||||||
|
if doc.get_api_key() != apikey:
|
||||||
|
raise Exception("Invalid API key")
|
||||||
|
|
||||||
output = ""
|
output = ""
|
||||||
output += "\n# Pulling source...\n"
|
output += "\n# Pulling source...\n"
|
||||||
output += doc.pull()
|
output += doc.pull()
|
||||||
|
15
src/app.py
15
src/app.py
@ -1,13 +1,28 @@
|
|||||||
import os
|
import os
|
||||||
|
import random
|
||||||
|
import string
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
import data.document
|
import data.document
|
||||||
|
import app_globals
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
src_path = os.path.dirname(os.path.realpath(__file__))
|
src_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
data.document.set_document_root(os.path.realpath(src_path+'/../data/doc'))
|
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
|
from api import api_document
|
||||||
app.register_blueprint(api_document.bp)
|
app.register_blueprint(api_document.bp)
|
||||||
|
1
src/app_globals.py
Normal file
1
src/app_globals.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
admin_password = None
|
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from web_utils.run import run
|
from web_utils.run import run
|
||||||
import shutil
|
import shutil
|
||||||
@ -62,6 +63,10 @@ class Document:
|
|||||||
def get_url(self):
|
def get_url(self):
|
||||||
return "/doc/" + sanitize_name(self.doc_name)+'/'+sanitize_name(self.branch) + "/index.html"
|
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
|
@staticmethod
|
||||||
def make_doc_path(doc_name, branch):
|
def make_doc_path(doc_name, branch):
|
||||||
doc_path = os.path.realpath(get_document_root()+'/'+sanitize_name(doc_name)+'/'+sanitize_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.
|
# 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://"):
|
if not repo.startswith("https://"):
|
||||||
raise Exception("Only HTTPS repositories are allowed in current implementation")
|
raise Exception("Only HTTPS repositories are allowed in current implementation")
|
||||||
|
|
||||||
|
# Generate an API key
|
||||||
|
apikey = str(uuid.uuid4())
|
||||||
|
|
||||||
target_dir = doc_path + "/repo"
|
target_dir = doc_path + "/repo"
|
||||||
|
|
||||||
cmd = ""
|
cmd = ""
|
||||||
cmd += "mkdir -p \"" + target_dir + "\"\n"
|
cmd += "mkdir -p \"" + target_dir + "\"\n"
|
||||||
|
cmd += "echo \""+apikey+"\" > \"" + doc_path + "/apikey\"\n"
|
||||||
cmd += "cd \"" + target_dir + "\"\n"
|
cmd += "cd \"" + target_dir + "\"\n"
|
||||||
cmd += "git init \"--initial-branch=" + branch + "\"\n"
|
cmd += "git init \"--initial-branch=" + branch + "\"\n"
|
||||||
cmd += "git remote add -f origin \"" + repo + "\"\n"
|
cmd += "git remote add -f origin \"" + repo + "\"\n"
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Gestion de {{ doc.doc_name }} / {{ doc.branch }}</h1>
|
<h1>Gestion de {{ doc.doc_name }} / {{ doc.branch }}</h1>
|
||||||
|
<p>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) }}</p>
|
||||||
<a href="{{doc.get_url()}}" class="button">Consulter</a><br/>
|
<a href="{{doc.get_url()}}" class="button">Consulter</a><br/>
|
||||||
<a href="{{ url_for('api_document.build', doc = doc.doc_name, branch = doc.branch) }}" class="button">Compiler</a><br/>
|
<a href="{{ url_for('api_document.build', doc = doc.doc_name, branch = doc.branch, apikey = doc.get_api_key()) }}" class="button">Compiler</a><br/>
|
||||||
<br/>
|
<br/>
|
||||||
<a href="{{ url_for('admin_document.delete', doc_name = doc.doc_name, branch = doc.branch) }}" class="button danger" data-confirm="Êtes-vous sûr de vouloir supprimer le document {{ doc.doc_name }} / {{ doc.branch }} ?">Supprimer</a>
|
<a href="{{ url_for('admin_document.delete', doc_name = doc.doc_name, branch = doc.branch) }}" class="button danger" data-confirm="Êtes-vous sûr de vouloir supprimer le document {{ doc.doc_name }} / {{ doc.branch }} ?">Supprimer</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
16
src/templates/admin/login.html
Normal file
16
src/templates/admin/login.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Documentation (admin)</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<form method="POST">
|
||||||
|
<label for="password">Mot de passe admin :</label> <input type="password" id="password" name="password"><br/>
|
||||||
|
|
||||||
|
<input type="submit"/>
|
||||||
|
</form>
|
||||||
|
</body>
|
@ -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
|
from data.document import Document
|
||||||
|
import app_globals
|
||||||
|
|
||||||
bp = Blueprint('admin', __name__, url_prefix='/admin')
|
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('/')
|
@bp.route('/')
|
||||||
def index():
|
def index():
|
||||||
return render_template("admin/index.html", documents=Document.list())
|
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")
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
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
|
from web_utils.get_arg import get_arg
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user