diff --git a/README.md b/README.md index 34868d9..740d862 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ Côté ScoDoc, créer un rôle et un utilisateur dédiés: flask create-role AutoSco flask edit-role AutoSco -a ScoView flask user-create autosco AutoSco @all +flask user-edit autosco --allow-scodoc-login flask user-password autosco + ``` Configurer les paramètres d'accès dans AutoSco: éditer le fichier diff --git a/app/__init__.py b/app/__init__.py index fa80920..f8ccff6 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,6 +1,10 @@ -from flask import current_app, g, request +import sys +import time + +from flask import current_app, g, render_template, request from flask import Flask +from scodoc.api import APIError, ScoDocAuthError from config import DevConfig @@ -18,6 +22,30 @@ class ReverseProxied: environ["wsgi.url_scheme"] = scheme # ou forcer à https ici ? return self.app(environ, start_response) +# --------- Logging +def log(msg: str): + """log a message. + If Flask app, use configured logger, else stderr. + """ + if current_app and not current_app.config["DEBUG"]: + current_app.logger.info(msg) + else: + sys.stdout.flush() + sys.stderr.write( + f"""[{time.strftime("%a %b %d %H:%M:%S %Y")}] scodoc: {msg}\n""" + ) + sys.stderr.flush() + +def handle_sco_api_error(exc): + "page d'erreur avec message" + log("Error: handle_sco_api_error") + return render_template("errors/sco_api_error.j2", exc=exc), 404 + +def handle_sco_auth_error(exc): + "page d'erreur avec message" + log("Error: handle_sco_auth_error") + return render_template("errors/sco_auth_error.j2", exc=exc), 404 + def create_app(config_class=DevConfig): app = Flask(__name__, static_url_path="/AutoSco/static", static_folder="static") @@ -27,4 +55,8 @@ def create_app(config_class=DevConfig): from app.views import bp app.register_blueprint(bp) + + app.register_error_handler(APIError, handle_sco_api_error) + app.register_error_handler(ScoDocAuthError, handle_sco_auth_error) + return app diff --git a/app/templates/errors/sco_api_error.j2 b/app/templates/errors/sco_api_error.j2 new file mode 100644 index 0000000..8d665c0 --- /dev/null +++ b/app/templates/errors/sco_api_error.j2 @@ -0,0 +1,13 @@ +{% extends 'base.j2' %} + +{% block content %} + +
+

Erreur API

+
ScoDoc a renvoyé une erreur {{exc.status_code}}
+
Message: {{exc.payload["message"]}}
+
Requête: {{exc.message}}
+
Si le problème persiste, veuillez contacter le support technique.
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/errors/sco_auth_error.j2 b/app/templates/errors/sco_auth_error.j2 new file mode 100644 index 0000000..402bd2e --- /dev/null +++ b/app/templates/errors/sco_auth_error.j2 @@ -0,0 +1,11 @@ +{% extends 'base.j2' %} + +{% block content %} + +
+

Erreur d'authentification

+
AutoSco ne parvient pas à se connecter à ScoDoc
+
Si le problème persiste, veuillez contacter le support technique.
+
+ +{% endblock %} \ No newline at end of file diff --git a/autosco.py b/autosco.py index c938aba..97d3dba 100755 --- a/autosco.py +++ b/autosco.py @@ -45,7 +45,6 @@ def make_shell_context(): "scu": scu, } +# Start using: +# flask run -p 5001 --host 0.0.0.0 --debug -if __name__ == "__main__": - port = os.environ.get("PORT", 5001) - app.run(debug=True, port=port) diff --git a/config.py b/config.py index a718315..f8457c0 100644 --- a/config.py +++ b/config.py @@ -40,7 +40,7 @@ class Config: SCODOC_CHECK_CERTIFICATE = os.environ.get("SCODOC_CHECK_CERTIFICATE", True) API_TIMEOUT = 120 # 2 minutes API_URL = SCODOC_URL + "/ScoDoc/api" - SCODOC_DEPT_ACRONYM = "ESPL" + SCODOC_DEPT_ACRONYM = os.environ.get("SCODOC_DEPT_ACRONYM", "ESPL") def __getitem__(self, k) -> str | int | None: return getattr(self, k) diff --git a/scodoc/api.py b/scodoc/api.py index 23ae7b3..f835597 100644 --- a/scodoc/api.py +++ b/scodoc/api.py @@ -7,20 +7,8 @@ from flask import current_app import config -def get_auth_headers(user: str, password: str, conf: config.Config) -> dict: - "Demande de jeton, dict à utiliser dans les en-têtes de requêtes http" - ans = requests.post( - conf["API_URL"] + "/tokens", - auth=(user, password), - timeout=conf["API_TIMEOUT"], - ) - if ans.status_code != 200: - raise APIError(f"Echec demande jeton par {user}", status_code=ans.status_code) - token = ans.json()["token"] - return {"Authorization": f"Bearer {token}"} - - class APIError(Exception): + "Error using ScoDoc API" def __init__(self, message: str = "", payload=None, status_code=None): self.message = message self.payload = payload or {} @@ -29,6 +17,20 @@ class APIError(Exception): def __str__(self): return f"APIError: {self.message} payload={self.payload} status_code={self.status_code}" +class ScoDocAuthError(APIError): + "Error getting ScoDoc token" + +def get_auth_headers(user: str, password: str, conf: config.Config) -> dict: + "Demande de jeton, dict à utiliser dans les en-têtes de requêtes http" + ans = requests.post( + conf["API_URL"] + "/tokens", + auth=(user, password), + timeout=conf["API_TIMEOUT"], + ) + if ans.status_code != 200: + raise ScoDocAuthError(f"Echec demande jeton par {user}", status_code=ans.status_code) + token = ans.json()["token"] + return {"Authorization": f"Bearer {token}"} class APIAccessor: "Gestion bas niveau des accès à l'API ScoDoc" @@ -64,9 +66,9 @@ class APIAccessor: print("url", url) print("reply", reply.text) try: - payload = r.json() + payload = reply.json() except requests.exceptions.JSONDecodeError: - payload = r.text + payload = reply.text raise APIError( errmsg or f"""erreur get {url} !""", payload,