Skip to content

Commit

Permalink
Merge pull request #103 from randombenj/bugfix/protect-override
Browse files Browse the repository at this point in the history
Bugfix/protect override
  • Loading branch information
fliiiix authored Jul 21, 2021
2 parents 4aa09f8 + 9ca3808 commit 49e7097
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 81 deletions.
57 changes: 37 additions & 20 deletions docat/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@
:copyright: (c) 2019 by docat, https://github.com/randombenj/docat
:license: MIT, see LICENSE for more details.
"""

import hashlib
import os
import secrets
from http import HTTPStatus
from pathlib import Path

from flask import Flask, request, send_from_directory
from tinydb import Query, TinyDB
from werkzeug.utils import secure_filename

from docat.docat.utils import UPLOAD_FOLDER, create_nginx_config, create_symlink, extract_archive, remove_docs
from docat.docat.utils import UPLOAD_FOLDER, calculate_token, create_nginx_config, create_symlink, extract_archive, remove_docs

app = Flask(__name__)
app.config["UPLOAD_FOLDER"] = os.getenv("DOCAT_DOC_PATH", UPLOAD_FOLDER)
app.config["UPLOAD_FOLDER"] = Path(os.getenv("DOCAT_DOC_PATH", UPLOAD_FOLDER))
app.config["MAX_CONTENT_LENGTH"] = 100 * 1024 * 1024 # 100M
app.db = TinyDB('db.json')

Expand All @@ -38,10 +37,18 @@ def upload(project, version):
base_path = project_base_path / version
target_file = base_path / secure_filename(uploaded_file.filename)

if base_path.exists():
token = request.headers.get("Docat-Api-Key")
result = check_token_for_project(token, project)
if result is True:
remove_docs(project, version)
else:
return result

# ensure directory for the uploaded doc exists
base_path.mkdir(parents=True, exist_ok=True)

# save the upploaded documentation
# save the uploaded documentation
uploaded_file.save(str(target_file))
extract_archive(target_file, base_path)

Expand Down Expand Up @@ -79,33 +86,43 @@ def claim(project):

token = secrets.token_hex(16)
salt = os.urandom(32)
token_hash = hashlib.pbkdf2_hmac("sha256", token.encode("utf-8"), salt, 100000)
token_hash = calculate_token(token, salt)
table.insert({"name": project, "token": token_hash, "salt": salt})

return {"message": f"Project {project} successfully claimed", "token": token}, HTTPStatus.CREATED


@app.route("/api/<project>/<version>", methods=["DELETE"])
def delete(project, version):
headers = request.headers
auth = headers.get("Docat-Api-Key")
token = request.headers.get("Docat-Api-Key")

result = check_token_for_project(token, project)
if result is True:
message = remove_docs(project, version)
if message:
return ({"message": message}, HTTPStatus.NOT_FOUND)
else:
return (
{"message": f"Successfully deleted version '{version}'"},
HTTPStatus.OK,
)
else:
return result


def check_token_for_project(token, project):
Project = Query()
table = app.db.table('claims')
result = table.search(Project.name == project)

if result and auth:
token_hash = hashlib.pbkdf2_hmac("sha256", auth.encode("utf-8"), result[0]["salt"], 100000)
print(f"stored hash: {result[0]['token']} calculated hash: {token_hash}")
if result and token:
token_hash = calculate_token(token, result[0]["salt"])
if result[0]["token"] == token_hash:
message = remove_docs(project, version)
if message:
return ({"message": message}, HTTPStatus.NOT_FOUND)
else:
return (
{"message": f"Successfully deleted version '{version}'"},
HTTPStatus.OK,
)
return ({"message": f"Please provide a header with a valid Docat-Api-Key token for {project}"}, HTTPStatus.UNAUTHORIZED)
return True
else:
return ({"message": f"Docat-Api-Key token is not valid for {project}"}, HTTPStatus.UNAUTHORIZED)
else:
return ({"message": f"Please provide a header with a valid Docat-Api-Key token for {project}"}, HTTPStatus.UNAUTHORIZED)


# serve_local_docs for local testing without a nginx
Expand Down
16 changes: 15 additions & 1 deletion docat/docat/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
docat utilities
"""
import hashlib
import os
import shutil
import subprocess
Expand Down Expand Up @@ -33,7 +34,7 @@ def create_symlink(source, destination):

def create_nginx_config(project, project_base_path):
"""
Creates an nginx configuration for an upploaded project
Creates an Nginx configuration for an uploaded project
version.
Args:
Expand Down Expand Up @@ -70,6 +71,7 @@ def extract_archive(target_file, destination):
def remove_docs(project, version):
"""
Delete documentation
Args:
project (str): name of the project
version (str): project version
Expand All @@ -90,3 +92,15 @@ def remove_docs(project, version):
nginx_config.unlink()
else:
return f"Could not find version '{docs}'"


def calculate_token(password, salt):
"""
Wrapper function for pbkdf2_hmac to ensure consistent use of
hash digest algorithm and iteration count.
Args:
password (str): the password to hash
salt (str): the salt used for the password
"""
return hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, 100000)
Loading

0 comments on commit 49e7097

Please sign in to comment.