premier commit

This commit is contained in:
Vincent BENOIT
2022-10-04 15:02:50 +02:00
parent b525495612
commit 10de8153aa
13 changed files with 700 additions and 0 deletions

17
Pipfile Normal file
View File

@@ -0,0 +1,17 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
flask = "*"
flask-api = "*"
flask-cors = "*"
jsonschema = "*"
pyjwt = "*"
flask-jwt-extended = "*"
[dev-packages]
[requires]
python_version = "3.7"

222
Pipfile.lock generated Normal file
View File

@@ -0,0 +1,222 @@
{
"_meta": {
"hash": {
"sha256": "aa2dcf776d27427f1752d52efb3fbb8ed86b3056f099f09c88c7ec7da77fdcae"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"attrs": {
"hashes": [
"sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6",
"sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"
],
"version": "==22.1.0"
},
"click": {
"hashes": [
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
"version": "==8.1.3"
},
"flask": {
"hashes": [
"sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b",
"sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"
],
"index": "pypi",
"version": "==2.2.2"
},
"flask-api": {
"hashes": [
"sha256:331889500433b0a5e71ae7910a00ee577c8999baba03ca685b3558ee93031cce",
"sha256:913d1ef4d303b5e4490a712175d83a91c2b9cc6052ef501cff8301d9f553b179"
],
"index": "pypi",
"version": "==3.0.post1"
},
"flask-cors": {
"hashes": [
"sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438",
"sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"
],
"index": "pypi",
"version": "==3.0.10"
},
"flask-jwt-extended": {
"hashes": [
"sha256:62b521d75494c290a646ae8acc77123721e4364790f1e64af0038d823961fbf0",
"sha256:a85eebfa17c339a7260c4643475af444784ba6de5588adda67406f0a75599553"
],
"index": "pypi",
"version": "==4.4.4"
},
"importlib-metadata": {
"hashes": [
"sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab",
"sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"
],
"markers": "python_version < '3.8'",
"version": "==5.0.0"
},
"importlib-resources": {
"hashes": [
"sha256:5481e97fb45af8dcf2f798952625591c58fe599d0735d86b10f54de086a61681",
"sha256:f78a8df21a79bcc30cfd400bdc38f314333de7c0fb619763f6b9dabab8268bb7"
],
"markers": "python_version < '3.9'",
"version": "==5.9.0"
},
"itsdangerous": {
"hashes": [
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
],
"version": "==2.1.2"
},
"jinja2": {
"hashes": [
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
],
"version": "==3.1.2"
},
"jsonschema": {
"hashes": [
"sha256:165059f076eff6971bae5b742fc029a7b4ef3f9bcf04c14e4776a7605de14b23",
"sha256:9e74b8f9738d6a946d70705dc692b74b5429cd0960d58e79ffecfc43b2221eb9"
],
"index": "pypi",
"version": "==4.16.0"
},
"markupsafe": {
"hashes": [
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
"version": "==2.1.1"
},
"pkgutil-resolve-name": {
"hashes": [
"sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174",
"sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"
],
"markers": "python_version < '3.9'",
"version": "==1.3.10"
},
"pyjwt": {
"hashes": [
"sha256:8d82e7087868e94dd8d7d418e5088ce64f7daab4b36db654cbaedb46f9d1ca80",
"sha256:e77ab89480905d86998442ac5788f35333fa85f65047a534adc38edf3c88fc3b"
],
"index": "pypi",
"version": "==2.5.0"
},
"pyrsistent": {
"hashes": [
"sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c",
"sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc",
"sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e",
"sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26",
"sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec",
"sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286",
"sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045",
"sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec",
"sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8",
"sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c",
"sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca",
"sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22",
"sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a",
"sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96",
"sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc",
"sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1",
"sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07",
"sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6",
"sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b",
"sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5",
"sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"
],
"version": "==0.18.1"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.16.0"
},
"typing-extensions": {
"hashes": [
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"markers": "python_version < '3.8'",
"version": "==4.3.0"
},
"werkzeug": {
"hashes": [
"sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f",
"sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"
],
"version": "==2.2.2"
},
"zipp": {
"hashes": [
"sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2",
"sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"
],
"markers": "python_version < '3.10'",
"version": "==3.8.1"
}
},
"develop": {}
}

6
db.json Normal file
View File

@@ -0,0 +1,6 @@
{
"utilisateur":{
"id" : "admin",
"password" : "28e55d1d25cd6e50cf4933c85636299367813225cecd7fd73c88282ed9e2c0d8"
}
}

10
run.py Normal file
View File

@@ -0,0 +1,10 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : Backend Configurateur KineInterCOM
from src import app
print("Launch Flask KineInterCOM Configurateur Backend ...")
application = app.create_app()
if application:
application.run(host="0.0.0.0", port=6000, use_reloader=False)

13
run_prod.py Normal file
View File

@@ -0,0 +1,13 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : KineIntercom Backend
from src import app
from src.config import ProdConfig
from waitress import serve
print("Launch Flask KineIntercom Backend ...")
application = app.create_app(config=ProdConfig)
if application:
serve(application, host="127.0.0.1", port=6000)

1
src/VERSION Normal file
View File

@@ -0,0 +1 @@
1.0.0

0
src/__init__.py Normal file
View File

131
src/app.py Normal file
View File

@@ -0,0 +1,131 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : Configurateur Backend Flask API
#########################################################
# Importation de modules externes #
import sys, re, os
import logging as log
from logging.config import dictConfig
from flask import Flask
from flask.logging import default_handler
from flask_cors import CORS, cross_origin
from flask_jwt_extended import JWTManager
import jwt
from src.config import DefaultConfig
from src.auth import auth
from src.log import log as logs
#########################################################
# Corps principal du programme #
def create_app(config=None, app_name=None):
''' Create and configure the app
:param config:
configuration object
:param app_name:
application name
'''
if app_name is None:
app_name = DefaultConfig.PROJECT
# create the app
# tells the app that configuration files are relative to the instance folder.
app = Flask(app_name,
instance_path=os.path.join(os.getcwd(), app_name),
instance_relative_config=True)
if not configure_app(app, config):
return None
configure_log(app)
configure_blueprints(app)
return app
def configure_app(app=None, config=None):
''' configure the application with configuration file and/or object
:param app:
Application object
:param config:
Configuration object
:return bool:
True if OK, otherwise False
'''
# load default config
app.config.from_object(DefaultConfig)
try:
if config:
# load specific config
app.config.from_object(config)
except ModuleNotFoundError as e:
print("Module de configuration non trouvé: {}".format(e))
return False
# setup the Flask-JWT-Extended extension
jwt = JWTManager(app)
# setup the Flask-CORS extension for handling Cross Origin Resource Sharing
CORS(app, resources={r"/api/*": {
# "origins": ["http://localhost:4200","http://localhost:5000"],
"origins": "*",
"supports_credentials": True
}})
return True
def configure_log(app=None):
''' Configure log handler
:param app:
Application object
:return None:
'''
if not os.path.exists(app.config['LOG_FOLDER']):
try:
os.makedirs(app.config['LOG_FOLDER'])
except OSError:
pass
# On vire tous les handlers
for h in app.logger.handlers:
app.logger.removeHandler(h)
# Set info level on logger, which might be overwritten by handers.
# Suppress DEBUG messages.
app.logger.setLevel(log.DEBUG)
formatter = log.Formatter('%(asctime)s - %(name)s [%(module)s.%(funcName)s:%(lineno)d] - %(levelname)s - %(message)s')
info_log = os.path.join(app.config['LOG_FOLDER'], DefaultConfig.PROJECT + '.log')
info_file_handler = log.handlers.RotatingFileHandler(info_log, maxBytes=100000, backupCount=10)
info_file_handler.setLevel(log.DEBUG)
info_file_handler.setFormatter(formatter)
app.logger.addHandler(info_file_handler)
fl = log.StreamHandler()
fl.setLevel(log.DEBUG)
fl.setFormatter(formatter)
app.logger.addHandler(fl)
#logging.getLogger('apscheduler').setLevel(logging.DEBUG)
def configure_blueprints(app=None):
''' configure blueprints
:param app:
Application object
:return None:
'''
for bp in [auth, logs]:
app.register_blueprint(bp)

6
src/auth/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : auth views and models
from .views import auth

148
src/auth/views.py Normal file
View File

@@ -0,0 +1,148 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : Auth routes
#########################################################
# Importation de modules externes #
import sys, re, os
import logging as log
from datetime import datetime, timezone
from flask import Flask, Blueprint, request, abort, jsonify, current_app
from flask_api import status
from flask_jwt_extended import create_access_token
from flask_jwt_extended import get_jwt
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import unset_jwt_cookies
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required, decode_token
import json
import shutil
import hashlib
from werkzeug.exceptions import HTTPException
#########################################################
# Class et Methods #
auth = Blueprint('auth', __name__, url_prefix='/api/configurateur')
@auth.errorhandler(HTTPException)
def handle_exception(e):
''' return JSON instead of HTML for HTTP errors '''
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
'code': e.code,
'name': e.name,
'description': e.description,
})
response.content_type = "application/json"
return response
@auth.after_request
def refresh_expiring_tokens(response):
''' Using an 'after_request' callback, we refresh any token that is within
30 minutes of expiring.'''
try:
exp_timestamp = get_jwt()['exp']
now = datetime.now(timezone.utc)
target_timestamp = datetime.timestamp(now + current_app.config['DELTA'])
if target_timestamp > exp_timestamp:
current_app.logger.warning("On doit recréer un token JWT ....")
access_token = create_access_token(identity=get_jwt_identity())
# refresh token in storage place
if os.path.exists(os.path.join("/tmp", current_app.config['PROJECT'])):
with open(os.path.join("/tmp", current_app.config['PROJECT'], get_jwt_identity()['id']), 'w') as f:
f.write(access_token)
# Modifiy a Flask Response to set a cookie containing the access JWT.
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
return response
@auth.route('/login', methods=['POST'])
def login():
''' Authenticate User '''
auth = request.authorization
current_app.logger.info("Connexion de l'utilisateur : {}".format(auth.username))
if not auth or not auth.username or not auth.password:
current_app.logger.error("Login and Password required !")
abort(401, description='Login and Password required')
try:
hashstr = hashlib.sha256(auth.password.encode('utf-8')).hexdigest()
with open(current_app.config['DB_PATH'], 'r') as f:
data = json.load(f)
# authenticate user with his username and password hash
if hashstr == data['utilisateur']['password']:
# create a new access token
current_app.logger.debug("create JWT Token with id: {}".format(auth.username))
token = create_access_token(identity={'id': auth.username})
if not os.path.exists(os.path.join("/tmp", current_app.config['PROJECT'])):
os.mkdir(os.path.join("/tmp", current_app.config['PROJECT']))
with open(os.path.join("/tmp", current_app.config['PROJECT'], auth.username), 'w') as f:
f.write(token)
content = jsonify({'message': 'login successful !'})
# set token as cookie
set_access_cookies(content, token)
return content, status.HTTP_200_OK
current_app.logger.error("Authorization failed !")
except Exception as e:
current_app.logger.error("Erreur : {}".format(e))
abort(500, description='Authentification impossible')
abort(401, description="Authorization failed !")
@auth.route('/logout', methods=['POST'])
@jwt_required()
def logout():
""" Handles HTTP requests to URL: /api/configurateur/logout """
current_app.logger.info("Deconnexion de l'utilisateur")
current_user = get_jwt_identity()
content = jsonify({'message': 'logout successful !'})
unset_jwt_cookies(content)
userpath = os.path.join("/tmp", current_app.config['PROJECT'])
if os.path.exists(userpath):
shutil.rmtree(userpath)
return content, status.HTTP_200_OK
@auth.route('/current', methods=['GET'])
@jwt_required()
def current_user():
''' retourne l'utilisateur courant connecté '''
# Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
return jsonify(current_user)
@auth.route('/userConnected', methods=['GET'])
def user_connected():
''' retourne "oui" si un utilisateur est déjà connecté sinon "non" '''
flag = False
resp = 'non'
uid = ''
path = os.path.join("/tmp", current_app.config['PROJECT'])
if os.path.exists(path):
# search unique file in path
for root, dirs, files in os.walk(path):
for f in files:
uid = str(f)
# retreive token in file
with open(os.path.join(path, f), 'r') as fp:
token = fp.read()
exp = decode_token(token, allow_expired = True)['exp']
now = datetime.now(timezone.utc)
target = datetime.timestamp(now)
if target > exp:
current_app.logger.warning("Le token de l'ancien utilisateur a expiré")
# remove tmp dir
shutil.rmtree(os.path.join("/tmp", current_app.config['PROJECT']))
flag = True
break
if not flag:
current_app.logger.warning("Un autre utilisateur est déjà connecté")
resp = 'oui'
content = ({'message':resp, 'uid':uid})
return jsonify(content), status.HTTP_200_OK

60
src/config.py Normal file
View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
#
# @author: vincent.benoit@benserv.fr
# @brief: Flask Config classes
import os
import datetime
class BaseConfig(object):
PROJECT = "configurateur"
# Get app root path, also can use flask.root_path.
PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
DEBUG = False
TESTING = False
FLASK_ENV = 'production'
ADMINS = ['vincent.benoit@benserv.fr']
LOG_FOLDER = os.path.join(os.getcwd(), 'log')
SECRET_KEY = "d]omg;<*|uHfs}ogN=dk_$YW"
# Setup the Flask-JWT-Extended extension
JWT_SECRET_KEY = "xbNNvwHCY*cN,)Yq;A,e(]|P"
JWT_COOKIE_SECURE = False
JWT_TOKEN_LOCATION = ["cookies"]
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(minutes=30)
DELTA = datetime.timedelta(minutes=20) # in minutes
# Controls if Cross Site Request Forgery (CSRF) protection is enabled when using cookies
# This should always be True in production
JWT_COOKIE_CSRF_PROTECT = True
JWT_CSRF_IN_COOKIES = True
STATIC_FOLDER = os.path.join(os.getcwd(), 'static')
UPLOAD_FOLDER = STATIC_FOLDER + '/upload'
DOWNLOAD_FOLDER = STATIC_FOLDER + '/download'
ALLOWED_EXTENSIONS = {'json', 'tar', 'txt'}
MAX_CONTENT_LENGTH = 200 * 1024 * 1024 # 100 megabytes
DB_PATH = os.path.join(os.getcwd(), 'db.json')
class DefaultConfig(BaseConfig):
DEBUG = True
TESTING = True
FLASK_ENV = 'development'
SECRET_KEY = "secretkey1"
# Setup the Flask-JWT-Extended extension
JWT_SECRET_KEY = "secretkeyjwt1"
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(minutes=10)
DELTA = datetime.timedelta(minutes=5) # in minutes
class ProdConfig(BaseConfig):
ROOT_BASE_FOLDER = "/opt"
# Log Folder path
LOG_FOLDER = os.path.join("/var/log", BaseConfig.PROJECT)

6
src/log/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : log views and models
from .views import log

80
src/log/views.py Normal file
View File

@@ -0,0 +1,80 @@
# -*- encoding: utf-8 -*-
# @author : vincent.benoit@benserv.fr
# @brief : log routes
#########################################################
# Importation de modules externes #
import sys, re, os
import logging as log
from datetime import datetime, timezone
from flask import Flask, Blueprint, request, abort, jsonify, current_app
from flask_api import status
from flask_jwt_extended import create_access_token
from flask_jwt_extended import get_jwt
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required
import json
from werkzeug.exceptions import HTTPException
#########################################################
# Class et Methods #
log = Blueprint('log', __name__, url_prefix='/api/configurateur')
@log.errorhandler(HTTPException)
def handle_exception(e):
''' return JSON instead of HTML for HTTP errors '''
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
'code': e.code,
'name': e.name,
'description': e.description,
})
response.content_type = "application/json"
return response
@log.after_request
def refresh_expiring_tokens(response):
''' Using an 'after_request' callback, we refresh any token that is within
30 minutes of expiring.'''
try:
exp_timestamp = get_jwt()['exp']
now = datetime.now(timezone.utc)
target_timestamp = datetime.timestamp(now + current_app.config['DELTA'])
if target_timestamp > exp_timestamp:
currenT_app.logger.warning("On doit recréer un token ....")
access_token = create_access_token(identity=get_jwt_identity())
# refresh token in storage place
if os.path.exists(os.path.join("/tmp", current_app.config['PROJECT'])):
with open(os.path.join("/tmp", current_app.config['PROJECT'], get_jwt_identity()['id']), 'w') as f:
f.write(access_token)
# Modifiy a Flask Response to set a cookie containing the access JWT.
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
return response
@log.route('/logs', methods=['GET'])
@jwt_required()
def get_all_logs():
''' Retreive app logs '''
# Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
lines = []
with open(os.path.join(current_app.config['LOG_FOLDER'], current_app.config['PROJECT'] + '.log')) as f:
for idx, line in enumerate(f.readlines()):
try:
date_time, head, gravity, msg = line.split(' - ', 3)
content = {'datetime': date_time, 'header': head, 'gravity': gravity, 'msg':msg}
lines.append(content)
except ValueError as e:
lines[-1]['msg'] = lines[-1]['msg'] + line
return jsonify(lines), status.HTTP_200_OK