From b9fc86c3c5f84bb4ca1baf542a78c50f626d4724 Mon Sep 17 00:00:00 2001 From: Antoine Goutenoir Date: Sun, 27 Oct 2019 21:55:37 +0100 Subject: [PATCH] Secure the admin a little. --- README.md | 2 ++ flaskr/__init__.py | 29 +++++++++++++++++++++++++++-- flaskr/controllers/main_controller.py | 22 +++++++++++++++------- flaskr/extensions.py | 2 ++ flaskr/templates/estimate.html | 6 +++--- requirements.txt | 2 ++ 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1f142f9..16cec38 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Tested only on Python `2.7`. _Sprint._ source venv/bin/activate pip install -r requirements.txt python manage.py createdb + cp .env.dist .env + nano .env ## Development diff --git a/flaskr/__init__.py b/flaskr/__init__.py index 08a1a12..83bd7b7 100755 --- a/flaskr/__init__.py +++ b/flaskr/__init__.py @@ -1,6 +1,10 @@ #! ../venv/bin/python +import os +from dotenv import load_dotenv, find_dotenv +load_dotenv(find_dotenv()) -from flask import Flask + +from flask import Flask, url_for from flask.cli import ScriptInfo from webassets.loaders import PythonLoader as PythonAssetsLoader @@ -14,7 +18,8 @@ from flaskr.extensions import ( cache, assets_env, debug_toolbar, - login_manager + login_manager, + basic_auth ) from flaskr.content import content @@ -48,6 +53,7 @@ def create_app(object_name): login_manager.init_app(app) admin.init_app(app) admin.add_view(EstimationView(Estimation, db.session)) + basic_auth.init_app(app) # Import and register the different asset bundles assets_env.init_app(app) @@ -58,6 +64,9 @@ def create_app(object_name): # register our blueprints app.register_blueprint(main) + app.config['BASIC_AUTH_USERNAME'] = os.getenv('ADMIN_USERNAME') + app.config['BASIC_AUTH_PASSWORD'] = os.getenv('ADMIN_PASSWORD') + # VERSION (move to version.py is necessary) version = "0.0.0" with open('VERSION', 'r') as version_file: @@ -76,4 +85,20 @@ def create_app(object_name): def markdown_filter(text): return markdown(text) + # Authentication Gate for the Admin + @app.before_first_request + def restrict_admin_url(): + + # print('VF', app.view_functions) + + endpoint = 'admin.index' + url = url_for(endpoint) + admin_index = app.view_functions.pop(endpoint) + + @app.route(url, endpoint=endpoint) + @basic_auth.required + # @roles_required('admin') + def secure_admin_index(): + return admin_index() + return app diff --git a/flaskr/controllers/main_controller.py b/flaskr/controllers/main_controller.py index 44e6332..e3c1127 100644 --- a/flaskr/controllers/main_controller.py +++ b/flaskr/controllers/main_controller.py @@ -6,7 +6,7 @@ from flask import Blueprint, render_template, flash, request, redirect, \ url_for, abort, send_from_directory from os.path import join -from flaskr.extensions import cache +from flaskr.extensions import cache, basic_auth from flaskr.forms import LoginForm, EstimateForm from flaskr.models import db, User, Estimation, StatusEnum from flaskr.geocoder import CachedGeocoder @@ -54,7 +54,7 @@ def estimate(): estimation.status = StatusEnum.pending estimation.origin_addresses = form.origin_addresses.data estimation.destination_addresses = form.destination_addresses.data - estimation.compute_optimal_destination = form.compute_optimal_destination.data + # estimation.compute_optimal_destination = form.compute_optimal_destination.data db.session.add(estimation) db.session.commit() @@ -376,8 +376,8 @@ def compute(): # process the queue of estimation requests return _respond(response) -@main.route("/estimation/.") -def consult_estimation(public_id, format): +@main.route("/estimation/.") +def consult_estimation(public_id, extension): try: estimation = Estimation.query \ .filter_by(public_id=public_id) \ @@ -394,7 +394,7 @@ def consult_estimation(public_id, format): unavailable_statuses = [StatusEnum.pending, StatusEnum.working] - if format in ['xhtml', 'html', 'htm']: + if extension in ['xhtml', 'html', 'htm']: if estimation.status in unavailable_statuses: return render_template( @@ -407,14 +407,14 @@ def consult_estimation(public_id, format): estimation=estimation ) - elif format in ['yaml', 'yml']: + elif extension in ['yaml', 'yml']: if estimation.status in unavailable_statuses: abort(404) return estimation.output_yaml - elif 'csv' == format: + elif 'csv' == extension: if estimation.status in unavailable_statuses: abort(404) @@ -447,3 +447,11 @@ def consult_estimation(public_id, format): else: abort(404) + + +@main.route("/test") +@basic_auth.required +def dev_test(): + import os + + return os.getenv('ADMIN_USERNAME') diff --git a/flaskr/extensions.py b/flaskr/extensions.py index 332aaeb..6edc215 100644 --- a/flaskr/extensions.py +++ b/flaskr/extensions.py @@ -1,4 +1,5 @@ from flask_admin import Admin +from flask_basicauth import BasicAuth from flask_caching import Cache from flask_debugtoolbar import DebugToolbarExtension from flask_login import LoginManager @@ -19,6 +20,7 @@ login_manager.login_view = "main.login" login_manager.login_message_category = "warning" admin = Admin() +basic_auth = BasicAuth() @login_manager.user_loader diff --git a/flaskr/templates/estimate.html b/flaskr/templates/estimate.html index e8d04c8..9e7bc1e 100644 --- a/flaskr/templates/estimate.html +++ b/flaskr/templates/estimate.html @@ -73,9 +73,9 @@ {{ render_field(form.destination_addresses) }} Provide multiple destinations to compare them. -
- {{ render_checkbox(form.compute_optimal_destination) }} -
+{#
#} +{# {{ render_checkbox(form.compute_optimal_destination) }}#} +{#
#}
{{ render_checkbox(form.use_atmosfair_rfi) }} Disabled. Work in Progress. RFI=1.9 diff --git a/requirements.txt b/requirements.txt index c4bd50d..664682c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ Flask==1.1.1 # Flask Extensions Flask-Admin==1.5.4 Flask-Assets==0.12 +Flask-BasicAuth==0.2.0 Flask-Caching==1.7.2 Flask-DebugToolbar==0.10.0 Flask-Login==0.4.0 @@ -19,6 +20,7 @@ Markdown==3.1.1 numpy==1.16.5 enum34==1.1.6 geopy==1.20.0 +python-dotenv==0.10.3 # Testing pytest==3.0.5 -- libgit2 0.21.2