diff --git a/ConfBack/app.py b/ConfBack/app.py index 9fdab81..7de26cd 100644 --- a/ConfBack/app.py +++ b/ConfBack/app.py @@ -21,6 +21,7 @@ from ConfBack.config import DefaultConfig from ConfBack.auth import auth from ConfBack.account import account from ConfBack.params import params +from ConfBack.datetime import dt from ConfBack.schedule import schedule from ConfBack.log import log as logs from ConfBack.infos import info @@ -135,5 +136,5 @@ def configure_blueprints(app=None): :return None: ''' - for bp in [auth, logs, account, params, schedule, info]: + for bp in [auth, logs, account, params, dt, schedule, info]: app.register_blueprint(bp) diff --git a/ConfBack/datetime/__init__.py b/ConfBack/datetime/__init__.py new file mode 100644 index 0000000..c476862 --- /dev/null +++ b/ConfBack/datetime/__init__.py @@ -0,0 +1,6 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : datetime views and models + +from .views import dt diff --git a/ConfBack/datetime/views.py b/ConfBack/datetime/views.py new file mode 100644 index 0000000..334a195 --- /dev/null +++ b/ConfBack/datetime/views.py @@ -0,0 +1,124 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : Datetime 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 + +import json +import shutil +import hashlib +import subprocess +from werkzeug.exceptions import HTTPException + +######################################################### +# Class et Methods # + +dt = Blueprint('datetime', __name__, url_prefix='/api/configurateur') + +@dt.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 + +@dt.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 + +def execute_cmd(args=""): + ''' Execute system command + ''' + out = None + err = None + if len(args) == 0: + current_app.logger.error("Paramètre d'entrée invalide") + return False + + current_app.logger.debug("La commande système executée est : {}".format(args)) + try: + cmd = subprocess.Popen(args, + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + (out, err) = cmd.communicate(timeout=10) + except subprocess.CalledProcessError as e: + current_app.logger.error("Error executing system command : {} - {}".format(e.output, e.returncode)) + return False + except subprocess.TimeoutExpired: + cmd.kill() + current_app.logger.error("Timeout when executing system command") + return False + except FileNotFoundError: + current_app.logger.error("System command not found") + return False + + if cmd.returncode != 0: + current_app.logger.error('Error executing system command ({})'.format(cmd.returncode)) + current_app.logger.error('{}'.format(err)) + return False + + return True + +@dt.route('/update_datetime', methods=['POST']) +@jwt_required() +def update_datetime(): + ''' Mise à jour de la date et heure du système + ''' + current_app.logger.info("Mise à jour de la date et heure du système") + current_user = get_jwt_identity() + # recuperation des attributs JSON de la requete + data_req = request.get_json() + current_app.logger.debug("Nouvelle date et heure: {}".format(data_req['datetime'])) + setdate_cmd = ['sudo', '/bin/date', '-s', data_req['datetime']] + if not execute_cmd(setdate_cmd): + abort(status.HTTP_500_INTERNAL_SERVER_ERROR, + description='Mise à l\'heure du système impossible') + + setdate_cmd = ['sudo', '/sbin/hwclock', '-w'] + if not execute_cmd(setdate_cmd): + abort(status.HTTP_500_INTERNAL_SERVER_ERROR, + description='Mise à l\'heure de l\horloge matérielle impossible') + + content = {'message':'maj date and time successful!'} + return content, status.HTTP_200_OK + diff --git a/ConfBack/infos/views.py b/ConfBack/infos/views.py index ba4d8dd..b8a67c4 100644 --- a/ConfBack/infos/views.py +++ b/ConfBack/infos/views.py @@ -146,3 +146,17 @@ def is_alive(): content = {'alive':ret} return jsonify(content), status.HTTP_200_OK + +@info.route('/rtc_alive', methods=['GET']) +@jwt_required() +def rtc_alive(): + ''' RTC is alive ? ''' + ret = False + if os.path.exists("/dev/rtc0"): + current_app.logger.info("L'horloge matérielle est présente ...") + ret = True + else: + current_app.logger.warning("L'horloge matérielle n'est pas présente ...") + + content = {'RTCalive':ret} + return jsonify(content), status.HTTP_200_OK