Commit b9fc86c3c5f84bb4ca1baf542a78c50f626d4724

Authored by Antoine Goutenoir
1 parent 16f69d07
Exists in master

Secure the admin a little.

@@ -18,6 +18,8 @@ Tested only on Python `2.7`. _Sprint._ @@ -18,6 +18,8 @@ Tested only on Python `2.7`. _Sprint._
18 source venv/bin/activate 18 source venv/bin/activate
19 pip install -r requirements.txt 19 pip install -r requirements.txt
20 python manage.py createdb 20 python manage.py createdb
  21 + cp .env.dist .env
  22 + nano .env
21 23
22 24
23 ## Development 25 ## Development
flaskr/__init__.py
1 #! ../venv/bin/python 1 #! ../venv/bin/python
  2 +import os
  3 +from dotenv import load_dotenv, find_dotenv
  4 +load_dotenv(find_dotenv())
2 5
3 -from flask import Flask 6 +
  7 +from flask import Flask, url_for
4 from flask.cli import ScriptInfo 8 from flask.cli import ScriptInfo
5 from webassets.loaders import PythonLoader as PythonAssetsLoader 9 from webassets.loaders import PythonLoader as PythonAssetsLoader
6 10
@@ -14,7 +18,8 @@ from flaskr.extensions import ( @@ -14,7 +18,8 @@ from flaskr.extensions import (
14 cache, 18 cache,
15 assets_env, 19 assets_env,
16 debug_toolbar, 20 debug_toolbar,
17 - login_manager 21 + login_manager,
  22 + basic_auth
18 ) 23 )
19 24
20 from flaskr.content import content 25 from flaskr.content import content
@@ -48,6 +53,7 @@ def create_app(object_name): @@ -48,6 +53,7 @@ def create_app(object_name):
48 login_manager.init_app(app) 53 login_manager.init_app(app)
49 admin.init_app(app) 54 admin.init_app(app)
50 admin.add_view(EstimationView(Estimation, db.session)) 55 admin.add_view(EstimationView(Estimation, db.session))
  56 + basic_auth.init_app(app)
51 57
52 # Import and register the different asset bundles 58 # Import and register the different asset bundles
53 assets_env.init_app(app) 59 assets_env.init_app(app)
@@ -58,6 +64,9 @@ def create_app(object_name): @@ -58,6 +64,9 @@ def create_app(object_name):
58 # register our blueprints 64 # register our blueprints
59 app.register_blueprint(main) 65 app.register_blueprint(main)
60 66
  67 + app.config['BASIC_AUTH_USERNAME'] = os.getenv('ADMIN_USERNAME')
  68 + app.config['BASIC_AUTH_PASSWORD'] = os.getenv('ADMIN_PASSWORD')
  69 +
61 # VERSION (move to version.py is necessary) 70 # VERSION (move to version.py is necessary)
62 version = "0.0.0" 71 version = "0.0.0"
63 with open('VERSION', 'r') as version_file: 72 with open('VERSION', 'r') as version_file:
@@ -76,4 +85,20 @@ def create_app(object_name): @@ -76,4 +85,20 @@ def create_app(object_name):
76 def markdown_filter(text): 85 def markdown_filter(text):
77 return markdown(text) 86 return markdown(text)
78 87
  88 + # Authentication Gate for the Admin
  89 + @app.before_first_request
  90 + def restrict_admin_url():
  91 +
  92 + # print('VF', app.view_functions)
  93 +
  94 + endpoint = 'admin.index'
  95 + url = url_for(endpoint)
  96 + admin_index = app.view_functions.pop(endpoint)
  97 +
  98 + @app.route(url, endpoint=endpoint)
  99 + @basic_auth.required
  100 + # @roles_required('admin')
  101 + def secure_admin_index():
  102 + return admin_index()
  103 +
79 return app 104 return app
flaskr/controllers/main_controller.py
@@ -6,7 +6,7 @@ from flask import Blueprint, render_template, flash, request, redirect, \ @@ -6,7 +6,7 @@ from flask import Blueprint, render_template, flash, request, redirect, \
6 url_for, abort, send_from_directory 6 url_for, abort, send_from_directory
7 from os.path import join 7 from os.path import join
8 8
9 -from flaskr.extensions import cache 9 +from flaskr.extensions import cache, basic_auth
10 from flaskr.forms import LoginForm, EstimateForm 10 from flaskr.forms import LoginForm, EstimateForm
11 from flaskr.models import db, User, Estimation, StatusEnum 11 from flaskr.models import db, User, Estimation, StatusEnum
12 from flaskr.geocoder import CachedGeocoder 12 from flaskr.geocoder import CachedGeocoder
@@ -54,7 +54,7 @@ def estimate(): @@ -54,7 +54,7 @@ def estimate():
54 estimation.status = StatusEnum.pending 54 estimation.status = StatusEnum.pending
55 estimation.origin_addresses = form.origin_addresses.data 55 estimation.origin_addresses = form.origin_addresses.data
56 estimation.destination_addresses = form.destination_addresses.data 56 estimation.destination_addresses = form.destination_addresses.data
57 - estimation.compute_optimal_destination = form.compute_optimal_destination.data 57 + # estimation.compute_optimal_destination = form.compute_optimal_destination.data
58 58
59 db.session.add(estimation) 59 db.session.add(estimation)
60 db.session.commit() 60 db.session.commit()
@@ -376,8 +376,8 @@ def compute(): # process the queue of estimation requests @@ -376,8 +376,8 @@ def compute(): # process the queue of estimation requests
376 return _respond(response) 376 return _respond(response)
377 377
378 378
379 -@main.route("/estimation/<public_id>.<format>")  
380 -def consult_estimation(public_id, format): 379 +@main.route("/estimation/<public_id>.<extension>")
  380 +def consult_estimation(public_id, extension):
381 try: 381 try:
382 estimation = Estimation.query \ 382 estimation = Estimation.query \
383 .filter_by(public_id=public_id) \ 383 .filter_by(public_id=public_id) \
@@ -394,7 +394,7 @@ def consult_estimation(public_id, format): @@ -394,7 +394,7 @@ def consult_estimation(public_id, format):
394 394
395 unavailable_statuses = [StatusEnum.pending, StatusEnum.working] 395 unavailable_statuses = [StatusEnum.pending, StatusEnum.working]
396 396
397 - if format in ['xhtml', 'html', 'htm']: 397 + if extension in ['xhtml', 'html', 'htm']:
398 398
399 if estimation.status in unavailable_statuses: 399 if estimation.status in unavailable_statuses:
400 return render_template( 400 return render_template(
@@ -407,14 +407,14 @@ def consult_estimation(public_id, format): @@ -407,14 +407,14 @@ def consult_estimation(public_id, format):
407 estimation=estimation 407 estimation=estimation
408 ) 408 )
409 409
410 - elif format in ['yaml', 'yml']: 410 + elif extension in ['yaml', 'yml']:
411 411
412 if estimation.status in unavailable_statuses: 412 if estimation.status in unavailable_statuses:
413 abort(404) 413 abort(404)
414 414
415 return estimation.output_yaml 415 return estimation.output_yaml
416 416
417 - elif 'csv' == format: 417 + elif 'csv' == extension:
418 418
419 if estimation.status in unavailable_statuses: 419 if estimation.status in unavailable_statuses:
420 abort(404) 420 abort(404)
@@ -447,3 +447,11 @@ def consult_estimation(public_id, format): @@ -447,3 +447,11 @@ def consult_estimation(public_id, format):
447 447
448 else: 448 else:
449 abort(404) 449 abort(404)
  450 +
  451 +
  452 +@main.route("/test")
  453 +@basic_auth.required
  454 +def dev_test():
  455 + import os
  456 +
  457 + return os.getenv('ADMIN_USERNAME')
flaskr/extensions.py
1 from flask_admin import Admin 1 from flask_admin import Admin
  2 +from flask_basicauth import BasicAuth
2 from flask_caching import Cache 3 from flask_caching import Cache
3 from flask_debugtoolbar import DebugToolbarExtension 4 from flask_debugtoolbar import DebugToolbarExtension
4 from flask_login import LoginManager 5 from flask_login import LoginManager
@@ -19,6 +20,7 @@ login_manager.login_view = &quot;main.login&quot; @@ -19,6 +20,7 @@ login_manager.login_view = &quot;main.login&quot;
19 login_manager.login_message_category = "warning" 20 login_manager.login_message_category = "warning"
20 21
21 admin = Admin() 22 admin = Admin()
  23 +basic_auth = BasicAuth()
22 24
23 25
24 @login_manager.user_loader 26 @login_manager.user_loader
flaskr/templates/estimate.html
@@ -73,9 +73,9 @@ @@ -73,9 +73,9 @@
73 {{ render_field(form.destination_addresses) }} 73 {{ render_field(form.destination_addresses) }}
74 <small class="form-text text-muted">Provide multiple destinations to compare them.</small> 74 <small class="form-text text-muted">Provide multiple destinations to compare them.</small>
75 </div> 75 </div>
76 - <div class="form-check form-group">  
77 - {{ render_checkbox(form.compute_optimal_destination) }}  
78 - </div> 76 +{# <div class="form-check form-group">#}
  77 +{# {{ render_checkbox(form.compute_optimal_destination) }}#}
  78 +{# </div>#}
79 <div class="form-check form-group"> 79 <div class="form-check form-group">
80 {{ render_checkbox(form.use_atmosfair_rfi) }} 80 {{ render_checkbox(form.use_atmosfair_rfi) }}
81 <small class="form-text text-muted">Disabled. Work in Progress. RFI=1.9</small> 81 <small class="form-text text-muted">Disabled. Work in Progress. RFI=1.9</small>
requirements.txt
@@ -3,6 +3,7 @@ Flask==1.1.1 @@ -3,6 +3,7 @@ Flask==1.1.1
3 # Flask Extensions 3 # Flask Extensions
4 Flask-Admin==1.5.4 4 Flask-Admin==1.5.4
5 Flask-Assets==0.12 5 Flask-Assets==0.12
  6 +Flask-BasicAuth==0.2.0
6 Flask-Caching==1.7.2 7 Flask-Caching==1.7.2
7 Flask-DebugToolbar==0.10.0 8 Flask-DebugToolbar==0.10.0
8 Flask-Login==0.4.0 9 Flask-Login==0.4.0
@@ -19,6 +20,7 @@ Markdown==3.1.1 @@ -19,6 +20,7 @@ Markdown==3.1.1
19 numpy==1.16.5 20 numpy==1.16.5
20 enum34==1.1.6 21 enum34==1.1.6
21 geopy==1.20.0 22 geopy==1.20.0
  23 +python-dotenv==0.10.3
22 24
23 # Testing 25 # Testing
24 pytest==3.0.5 26 pytest==3.0.5