From a66406eea43d4273b082118d85773b055f3cbb34 Mon Sep 17 00:00:00 2001 From: Camille Monchicourt Date: Tue, 27 Jun 2023 00:11:20 +0200 Subject: [PATCH 01/15] Back to DEV --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3f684d2..69c4861 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.4 +2.3.5.dev0 From 0f0041b91d3913fc5500aac5670625a7e964f772 Mon Sep 17 00:00:00 2001 From: "joel.clement" Date: Mon, 3 Jul 2023 14:26:37 +0200 Subject: [PATCH 02/15] docker healthcheck && relaod on config.py --- Dockerfile | 5 ++++- docker_healthcheck.sh | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 docker_healthcheck.sh diff --git a/Dockerfile b/Dockerfile index bead48b..095caa9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,6 +50,7 @@ RUN --mount=type=cache,target=/root/.cache \ pip install --upgrade pip setuptools wheel COPY /setup.py . +COPY --chmod=755 /docker_healthcheck.sh . COPY /requirements-common.in . COPY /requirements-dependencies.in . COPY /VERSION . @@ -113,6 +114,8 @@ ENV PYTHONPATH=/dist/config/ ENV USERSHUB_SETTINGS=config.py ENV USERSHUB_STATIC_FOLDER=/dist/static +COPY --chmod=755 /docker_healthcheck.sh . + EXPOSE 5001 -CMD ["gunicorn", "app.app:create_app()", "--bind=0.0.0.0:5001", "--access-logfile=-", "--error-logfile=-"] +CMD ["gunicorn", "app.app:create_app()", "--bind=0.0.0.0:5001", "--access-logfile=-", "--error-logfile=-", "--reload", "--reload-extra-file=config/config.py"] diff --git a/docker_healthcheck.sh b/docker_healthcheck.sh new file mode 100644 index 0000000..189e1e1 --- /dev/null +++ b/docker_healthcheck.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# docker healthcheck UH +url_test=http://localhost:5001/usershub/login +if [ ! -f /tmp/container_healthy ]; then + curl -f "${url_test}" || exit 1 + touch /tmp/container_healthy +fi \ No newline at end of file From bb885b35d428c194521c26815e9fed9f180ed818 Mon Sep 17 00:00:00 2001 From: "joel.clement" Date: Thu, 13 Jul 2023 12:41:34 +0200 Subject: [PATCH 03/15] up healthcheck --- docker_healthcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker_healthcheck.sh b/docker_healthcheck.sh index 189e1e1..3f5ac51 100644 --- a/docker_healthcheck.sh +++ b/docker_healthcheck.sh @@ -1,6 +1,6 @@ #!/bin/bash # docker healthcheck UH -url_test=http://localhost:5001/usershub/login +url_test=http://localhost:5001${USERSHUB_APPLICATION_ROOT}/login if [ ! -f /tmp/container_healthy ]; then curl -f "${url_test}" || exit 1 touch /tmp/container_healthy From 6c4c0b324eef280e57d6fe74ee1dd574a43ad008 Mon Sep 17 00:00:00 2001 From: TheoLechemia Date: Thu, 14 Sep 2023 16:30:12 +0200 Subject: [PATCH 04/15] remove deprecated parameters on check_auth --- app/api/route.py | 20 +++--- app/api/route_register.py | 71 +++++++++---------- app/app.py | 55 +++++++------- app/bib_organismes/route.py | 26 +++---- app/groupe/route.py | 32 ++++----- app/liste/route.py | 28 +++----- app/login/route.py | 16 ++--- app/t_applications/route.py | 45 ++++-------- app/t_profils/route.py | 23 +++--- app/t_roles/route.py | 26 ++----- app/temp_users/routes.py | 23 +++--- dependencies/UsersHub-authentification-module | 2 +- 12 files changed, 153 insertions(+), 214 deletions(-) diff --git a/app/api/route.py b/app/api/route.py index a18b88d..87fc2c4 100644 --- a/app/api/route.py +++ b/app/api/route.py @@ -3,25 +3,23 @@ from app.env import db from app.utils.utilssqlalchemy import json_resp -from app.models import ( - TProfils, CorProfilForApp -) +from app.models import TProfils, CorProfilForApp -route = Blueprint('api', __name__) +route = Blueprint("api", __name__) -@route.route('/profils', methods=["GET"]) +@route.route("/profils", methods=["GET"]) @json_resp def get_profils(): - ''' - Return the profils - ''' + """ + Return the profils + """ params = request.args q = db.session.query(TProfils) - if 'id_application' in params: + if "id_application" in params: q = q.join( CorProfilForApp, CorProfilForApp.id_profil == TProfils.id_profil - ).filter(CorProfilForApp.id_application == params['id_application']) - data = [data.as_dict(columns=['id_profil', 'nom_profil']) for data in q.all()] + ).filter(CorProfilForApp.id_application == params["id_application"]) + data = [data.as_dict(columns=["id_profil", "nom_profil"]) for data in q.all()] return data diff --git a/app/api/route_register.py b/app/api/route_register.py index 4a732db..2b2b109 100644 --- a/app/api/route_register.py +++ b/app/api/route_register.py @@ -21,11 +21,11 @@ @route.route("/role/", methods=["GET", "POST"]) -@fnauth.check_auth(1, False, "/api_register/role_check_auth_error") +@fnauth.check_auth(1) @json_resp def get_one_t_roles(id_role): """ - Fonction qui retourne les données concernant un utilisateur. + Fonction qui retourne les données concernant un utilisateur. """ role = TRoles.get_one(id_role) @@ -37,7 +37,7 @@ def get_one_t_roles(id_role): @json_resp def test_connexion(): """ - Route pour tester la connexion. + Route pour tester la connexion. """ return {"msg": "connexion ok"} @@ -47,14 +47,14 @@ def test_connexion(): @json_resp def create_temp_user(): """ - Route pour créer un compte temporaire en attendant la confirmation de - l'adresse mail. - - Nous stockons : - 1. Les infos qui seront utilisées par la création de compte. Dont les - mots de passe qui sont stockés cryptés. - 2. Les infos permettant d'appeler l'appli source si la création du - compte est confirmée (Appel d'une URL de callaback => confirmation_url). + Route pour créer un compte temporaire en attendant la confirmation de + l'adresse mail. + + Nous stockons : + 1. Les infos qui seront utilisées par la création de compte. Dont les + mots de passe qui sont stockés cryptés. + 2. Les infos permettant d'appeler l'appli source si la création du + compte est confirmée (Appel d'une URL de callaback => confirmation_url). """ # Get data from JSON @@ -110,8 +110,8 @@ def create_temp_user(): @json_resp def valid_temp_user(): """ - Route pour valider un compte temporaire et en faire un utilisateur - (requete a usershub). + Route pour valider un compte temporaire et en faire un utilisateur + (requete a usershub). """ data_in = request.get_json() @@ -159,8 +159,8 @@ def valid_temp_user(): def set_cor_role_token(email): """ - Fonction pour la création d'un token associé a un id_role - Parametres : email + Fonction pour la création d'un token associé a un id_role + Parametres : email """ if not email: return {"msg": "Aucun email"}, 404 @@ -188,8 +188,8 @@ def set_cor_role_token(email): def check_token(token): """ - fonction permettant de vérifier la présence - d'un token et qui retourne l'id_role associé + fonction permettant de vérifier la présence + d'un token et qui retourne l'id_role associé """ res = ( db.session.query(CorRoleToken.id_role) @@ -207,9 +207,9 @@ def check_token(token): @json_resp def create_cor_role_token(): """ - route pour la creation d'un token associé a un id_role - fait un appel de la fonction set_cor_role_token(email) - parametres post : email + route pour la creation d'un token associé a un id_role + fait un appel de la fonction set_cor_role_token(email) + parametres post : email """ data = request.get_json() @@ -224,8 +224,8 @@ def create_cor_role_token(): @json_resp def change_password(): """ - Route permettant à un utilisateur de renouveller - son mot de passe + Route permettant à un utilisateur de renouveller + son mot de passe """ data = request.get_json() @@ -265,7 +265,7 @@ def change_password(): @json_resp def change_application_right(): """ - Change les droits d'un utilisateur pour une application + Change les droits d'un utilisateur pour une application """ req_data = request.get_json() @@ -333,10 +333,10 @@ def change_application_right(): @json_resp def add_application_right_to_role(): """ - Route permettant de d'ajouter des droits - pour une application a un utilisateur - soit en l'associant à un groupe - soit en lui affectant le profil "1" + Route permettant de d'ajouter des droits + pour une application a un utilisateur + soit en l'associant à un groupe + soit en lui affectant le profil "1" """ req_data = request.get_json() @@ -365,7 +365,6 @@ def add_application_right_to_role(): pwd_hash = hashlib.md5(pwd.encode("utf-8")).hexdigest() if not role.pass_md5 == pwd_hash: - return {"msg": "password false"}, 500 # Test si l'utilisateur n'est pas déjà associé au groupe @@ -383,10 +382,10 @@ def add_application_right_to_role(): @json_resp def login_recovery(): """ - route pour changer des paramètres d'utilisateur - FIXME : Route qui ne modifie rien du tout - devrait peut être transformée pour être plus générique - et retourner les informations d'un utilisateur donné + route pour changer des paramètres d'utilisateur + FIXME : Route qui ne modifie rien du tout + devrait peut être transformée pour être plus générique + et retourner les informations d'un utilisateur donné """ req_data = request.get_json() @@ -424,14 +423,13 @@ def login_recovery(): @json_resp def update_user(): """ - route pour changer des paramètres d'utilisateur + route pour changer des paramètres d'utilisateur """ req_data = request.get_json() id_role = req_data.get("id_role", None) if not id_role: - return {"msg": "Pas d'id_role"}, 400 role_data = {} @@ -450,8 +448,8 @@ def update_user(): @json_resp def check_token_validity(): """ - route permettant de savoir si un token est toujours valide - parametres post : token + route permettant de savoir si un token est toujours valide + parametres post : token """ data = request.get_json() @@ -461,4 +459,3 @@ def check_token_validity(): return {"msg": "valid token"}, 200 return {"msg": "invalid token"}, 500 - diff --git a/app/app.py b/app/app.py index 92fe8dd..9bd208f 100644 --- a/app/app.py +++ b/app/app.py @@ -10,11 +10,7 @@ from urllib.parse import urlsplit from pathlib import Path -from flask import ( - Flask, redirect, url_for, - request, session, render_template, - g -) +from flask import Flask, redirect, url_for, request, session, render_template, g from werkzeug.middleware.proxy_fix import ProxyFix from sqlalchemy.exc import ProgrammingError from flask_migrate import Migrate @@ -22,6 +18,7 @@ from app.env import db from pypnusershub.db.models import Application +from pypnusershub.login_manager import login_manager migrate = Migrate() @@ -34,29 +31,34 @@ def configure_alembic(alembic_config): 'migrations' entry point value of the 'gn_module' group for all modules having such entry point. Thus, alembic will find migrations of all installed geonature modules. """ - version_locations = alembic_config.get_main_option('version_locations', default='').split() - for entry_point in iter_entry_points('alembic', 'migrations'): - _, migrations = str(entry_point).split('=', 1) - version_locations += [ migrations.strip() ] - alembic_config.set_main_option('version_locations', ' '.join(version_locations)) + version_locations = alembic_config.get_main_option( + "version_locations", default="" + ).split() + for entry_point in iter_entry_points("alembic", "migrations"): + _, migrations = str(entry_point).split("=", 1) + version_locations += [migrations.strip()] + alembic_config.set_main_option("version_locations", " ".join(version_locations)) return alembic_config def create_app(): - app = Flask(__name__, static_folder=os.environ.get("USERSHUB_STATIC_FOLDER", "static")) + app = Flask( + __name__, static_folder=os.environ.get("USERSHUB_STATIC_FOLDER", "static") + ) app.config.from_pyfile(os.environ.get("USERSHUB_SETTINGS", "../config/config.py")) app.config.from_prefixed_env(prefix="USERSHUB") - app.config['APPLICATION_ROOT'] = urlsplit(app.config['URL_APPLICATION']).path or '/' - if 'SCRIPT_NAME' not in os.environ and app.config['APPLICATION_ROOT'] != '/': - os.environ['SCRIPT_NAME'] = app.config['APPLICATION_ROOT'] - app.config["URL_REDIRECT"] = "{}/{}".format(app.config["URL_APPLICATION"], "login") + app.config["APPLICATION_ROOT"] = urlsplit(app.config["URL_APPLICATION"]).path or "/" + if "SCRIPT_NAME" not in os.environ and app.config["APPLICATION_ROOT"] != "/": + os.environ["SCRIPT_NAME"] = app.config["APPLICATION_ROOT"] + app.config["URL_REDIRECT"] = "{}/{}".format(app.config["URL_APPLICATION"], "login") app.secret_key = app.config["SECRET_KEY"] app.wsgi_app = ProxyFix(app.wsgi_app, x_host=1) - + login_manager.init_app(app) + login_manager.login_view = "login.login" db.init_app(app) app.config["DB"] = db - migrate.init_app(app, db, directory=Path(__file__).absolute().parent / 'migrations') + migrate.init_app(app, db, directory=Path(__file__).absolute().parent / "migrations") if "CODE_APPLICATION" not in app.config: app.config["CODE_APPLICATION"] = "UH" @@ -65,26 +67,29 @@ def create_app(): app.jinja_env.globals["url_application"] = app.config["URL_APPLICATION"] if app.config["ACTIVATE_APP"]: + @app.route("/") def index(): - """ Route par défaut de l'application """ + """Route par défaut de l'application""" return redirect(url_for("user.users")) @app.route("/constants.js") def constants_js(): - """ Route des constantes javascript """ + """Route des constantes javascript""" return render_template("constants.js") @app.after_request def after_login_method(response): """ - Fonction s'exécutant après chaque requete - permet de gérer l'authentification + Fonction s'exécutant après chaque requete + permet de gérer l'authentification """ if not request.cookies.get("token"): session["current_user"] = None - if request.endpoint == "auth.login" and response.status_code == 200: # noqa + if ( + request.endpoint == "auth.login" and response.status_code == 200 + ): # noqa current_user = json.loads(response.get_data().decode("utf-8")) session["current_user"] = current_user["user"] return response @@ -133,9 +138,11 @@ def inject_user(): app.register_blueprint(route.route, url_prefix="/api") - if app.config['ACTIVATE_API']: + if app.config["ACTIVATE_API"]: from app.api import route_register - app.register_blueprint(route_register.route, url_prefix="/api_register") # noqa + app.register_blueprint( + route_register.route, url_prefix="/api_register" + ) # noqa return app diff --git a/app/bib_organismes/route.py b/app/bib_organismes/route.py index cdb6a56..cb9d5c3 100644 --- a/app/bib_organismes/route.py +++ b/app/bib_organismes/route.py @@ -3,7 +3,13 @@ """ from flask import ( - Blueprint, redirect, url_for, render_template, request, flash, current_app + Blueprint, + redirect, + url_for, + render_template, + request, + flash, + current_app, ) from pypnusershub import routes as fnauth @@ -14,20 +20,16 @@ from app.utils.utils_all import strigify_dict -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_REDIRECT = current_app.config["URL_REDIRECT"] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("organisme", __name__) @route.route("organisms/list", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def organisms(): - """ Route qui affiche la liste des Organismes Retourne un template avec pour paramètres : @@ -87,9 +89,6 @@ def organisms(): @route.route("organism/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_organisme): """ @@ -127,9 +126,6 @@ def addorupdate(id_organisme): @route.route("organisms/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete(id_organisme): """ @@ -144,9 +140,6 @@ def delete(id_organisme): @route.route("organism/info/", methods=["GET"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def info(id_organisme): org = Bib_Organismes.get_one(id_organisme) @@ -164,7 +157,6 @@ def info(id_organisme): def pops(form): - """ Methode qui supprime les éléments indésirables du formulaires Avec pour paramètre un formulaire diff --git a/app/groupe/route.py b/app/groupe/route.py index 45666e4..84dd34c 100644 --- a/app/groupe/route.py +++ b/app/groupe/route.py @@ -1,4 +1,13 @@ -from flask import redirect, url_for, render_template, Blueprint, request, flash, jsonify, current_app +from flask import ( + redirect, + url_for, + render_template, + Blueprint, + request, + flash, + jsonify, + current_app, +) from pypnusershub import routes as fnauth from app.groupe import forms as groupeforms @@ -7,8 +16,8 @@ from app.utils.utils_all import strigify_dict -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_REDIRECT = current_app.config["URL_REDIRECT"] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("groupe", __name__) @@ -16,12 +25,8 @@ @route.route("groups/list", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def groups(): - """ Route qui affiche la liste des groupes Retourne un template avec pour paramètres : @@ -66,12 +71,8 @@ def groups(): @route.route("group/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_role=None): - """ Route affichant un formulaire vierge ou non (selon l'url) pour ajouter ou mettre à jour un groupe L'envoie du formulaire permet l'ajout ou la maj du groupe dans la base @@ -111,9 +112,6 @@ def addorupdate(id_role=None): @route.route("group/members/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def membres(id_groupe): """ @@ -156,9 +154,6 @@ def membres(id_groupe): @route.route("group/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete(id_groupe): """ @@ -173,9 +168,6 @@ def delete(id_groupe): @route.route("group/info/", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def info(id_role): group = TRoles.get_one(id_role) diff --git a/app/liste/route.py b/app/liste/route.py index b26f459..ec4cd31 100644 --- a/app/liste/route.py +++ b/app/liste/route.py @@ -1,6 +1,12 @@ from flask import ( - redirect, url_for, render_template, - Blueprint, request, flash, jsonify, current_app + redirect, + url_for, + render_template, + Blueprint, + request, + flash, + jsonify, + current_app, ) from pypnusershub import routes as fnauth @@ -9,8 +15,8 @@ from app.models import TListes, CorRoleListe, TRoles from app.utils.utils_all import strigify_dict -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_REDIRECT = current_app.config["URL_REDIRECT"] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("liste", __name__) @@ -19,9 +25,6 @@ @route.route("lists/list", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def lists(): """ @@ -67,9 +70,6 @@ def lists(): @route.route("list/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_liste): """ @@ -106,9 +106,6 @@ def addorupdate(id_liste): @route.route("list/members/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def membres(id_liste): """ @@ -149,9 +146,6 @@ def membres(id_liste): @route.route("list/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete(id_liste): """ @@ -163,7 +157,7 @@ def delete(id_liste): @route.route("list/info/", methods=["GET"]) -@fnauth.check_auth(3, False, URL_REDIRECT) +@fnauth.check_auth(3) def info(id_liste): mylist = TListes.get_one(id_liste) members = ( diff --git a/app/login/route.py b/app/login/route.py index a1fabd5..d2f412d 100644 --- a/app/login/route.py +++ b/app/login/route.py @@ -1,14 +1,14 @@ -from flask import ( - Flask, redirect, url_for, render_template, - Blueprint, current_app -) +from flask import Flask, redirect, url_for, render_template, Blueprint, current_app from app.env import db from app.models import TApplications -route = Blueprint('login', __name__) +route = Blueprint("login", __name__) -@route.route('login', methods=['GET']) + +@route.route("login", methods=["GET"]) def login(): - app = TApplications.query.filter_by(code_application=current_app.config["CODE_APPLICATION"]).one() - return render_template('login.html', id_app=app.id_application) + app = TApplications.query.filter_by( + code_application=current_app.config["CODE_APPLICATION"] + ).one() + return render_template("login.html", id_app=app.id_application) diff --git a/app/t_applications/route.py b/app/t_applications/route.py index c288426..697bcc9 100644 --- a/app/t_applications/route.py +++ b/app/t_applications/route.py @@ -6,7 +6,7 @@ request, flash, jsonify, - current_app + current_app, ) from app.env import db @@ -22,8 +22,8 @@ from pypnusershub import routes as fnauth -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_REDIRECT = current_app.config["URL_REDIRECT"] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("application", __name__) @@ -31,9 +31,6 @@ @route.route("applications/list", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def applications(): """ @@ -91,7 +88,7 @@ def applications(): @route.route("application/info/", methods=["GET"]) -@fnauth.check_auth(3, False, URL_REDIRECT) +@fnauth.check_auth(3) def info(id_application): """ Route qui retourne une fiche de l'application @@ -117,16 +114,13 @@ def info(id_application): @route.route("application/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_application): """ - Route affichant un formulaire vierge ou non (selon l'url) pour ajouter ou mettre à jour une application - L'envoie du formulaire permet l'ajout ou la maj d'une application dans la base - Retourne un template accompagné d'un formulaire pré-rempli ou non selon le paramètre id_application - Une fois le formulaire validé on retourne une redirection vers la liste d'application + Route affichant un formulaire vierge ou non (selon l'url) pour ajouter ou mettre à jour une application + L'envoie du formulaire permet l'ajout ou la maj d'une application dans la base + Retourne un template accompagné d'un formulaire pré-rempli ou non selon le paramètre id_application + Une fois le formulaire validé on retourne une redirection vers la liste d'application """ form = t_applicationsforms.Application() form.id_parent.choices = TApplications.choixSelect( @@ -177,9 +171,6 @@ def addorupdate(id_application): @route.route("application/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete(id_application): """ @@ -193,9 +184,6 @@ def delete(id_application): @route.route("application/profils/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def profils_for_app(id_application): """ @@ -235,9 +223,6 @@ def profils_for_app(id_application): @route.route("application_roles_profil/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def profils_in_app(id_application): """ @@ -286,16 +271,13 @@ def profils_in_app(id_application): ) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def add_or_update_profil_for_role_in_app(id_application, id_role=None): """ - add or update un profil sur une application - on part du principe qu'un role ne peut avoir qu'un profil dans une application - TODO: pour mettre plusieurs profil a un role dans une appli: - rajouter une clé primaire a cor_role_app_profil pour gérer l'update + add or update un profil sur une application + on part du principe qu'un role ne peut avoir qu'un profil dans une application + TODO: pour mettre plusieurs profil a un role dans une appli: + rajouter une clé primaire a cor_role_app_profil pour gérer l'update """ form = t_applicationsforms.AppProfil(id_application) application = TApplications.get_one(id_application) @@ -359,9 +341,6 @@ def add_or_update_profil_for_role_in_app(id_application, id_role=None): ) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete_cor_role_app_profil(id_role, id_application): try: diff --git a/app/t_profils/route.py b/app/t_profils/route.py index 0e51793..affd0c2 100644 --- a/app/t_profils/route.py +++ b/app/t_profils/route.py @@ -1,4 +1,12 @@ -from flask import redirect, url_for, render_template, Blueprint, request, flash, current_app +from flask import ( + redirect, + url_for, + render_template, + Blueprint, + request, + flash, + current_app, +) from pypnusershub import routes as fnauth @@ -12,8 +20,8 @@ CorRoleAppProfil, ) -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_REDIRECT = current_app.config["URL_REDIRECT"] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("profils", __name__) @@ -25,9 +33,6 @@ @route.route("profils/list", methods=["GET", "POST"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def profils(): """ @@ -70,9 +75,6 @@ def profils(): @route.route("profil/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def delete(id_profil): """ @@ -88,9 +90,6 @@ def delete(id_profil): @route.route("profil/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_profil): """ diff --git a/app/t_roles/route.py b/app/t_roles/route.py index 8dc27dc..5c9ef7d 100644 --- a/app/t_roles/route.py +++ b/app/t_roles/route.py @@ -6,7 +6,7 @@ Blueprint, request, flash, - current_app + current_app, ) from pypnusershub import routes as fnauth @@ -19,8 +19,7 @@ from app.env import db -URL_REDIRECT = current_app.config['URL_REDIRECT'] -URL_APPLICATION = current_app.config['URL_APPLICATION'] +URL_APPLICATION = current_app.config["URL_APPLICATION"] route = Blueprint("user", __name__) @@ -28,13 +27,8 @@ @route.route("users/list", methods=["GET"]) @fnauth.check_auth( 3, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, - redirect_on_insufficient_right=URL_REDIRECT, ) def users(): - """ Route qui affiche la liste des utilisateurs Retourne un template avec pour paramètres : @@ -116,9 +110,6 @@ def users(): @route.route("user/update/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def addorupdate(id_role=None): """ @@ -206,9 +197,6 @@ def addorupdate(id_role=None): @route.route("user/pass/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def updatepass(id_role=None): """ @@ -269,9 +257,6 @@ def updatepass(id_role=None): @route.route("users/delete/", methods=["GET", "POST"]) @fnauth.check_auth( 6, - False, - redirect_on_expiration=URL_REDIRECT, - redirect_on_invalid_token=URL_REDIRECT, ) def deluser(id_role): """ @@ -283,10 +268,12 @@ def deluser(id_role): @route.route("user/info/", methods=["GET", "POST"]) -@fnauth.check_auth(6, False, URL_REDIRECT) +@fnauth.check_auth(6) def info(id_role): user = TRoles.get_one(id_role) - organisme = Bib_Organismes.get_one(user["id_organisme"]) if user['id_organisme'] else None + organisme = ( + Bib_Organismes.get_one(user["id_organisme"]) if user["id_organisme"] else None + ) groups = TRoles.get_user_groups(id_role) lists = TRoles.get_user_lists(id_role) rights = TRoles.get_user_app_profils(id_role) @@ -337,4 +324,3 @@ def test(id_role): tab = [{"test1": "test1", "test2": "Test 1"}, {"test1": "test2", "test2": "Test 2"}] return render_template("generic_table.html", fLine=fLine, table=tab) - diff --git a/app/temp_users/routes.py b/app/temp_users/routes.py index 7e486d0..3d9cf49 100644 --- a/app/temp_users/routes.py +++ b/app/temp_users/routes.py @@ -9,7 +9,7 @@ redirect, url_for, flash, - current_app + current_app, ) from pypnusershub.db.models_register import TempUser from pypnusershub import routes as fnauth @@ -19,14 +19,12 @@ from app.models import TApplications -URL_REDIRECT = current_app.config['URL_REDIRECT'] - routes = Blueprint("temp_users", __name__) log = logging.getLogger() @routes.route("/list", methods=["GET"]) -@fnauth.check_auth(6, False, URL_REDIRECT) +@fnauth.check_auth(6) def temp_users_list(): """ Get all temp_users @@ -52,7 +50,7 @@ def temp_users_list(): @routes.route("/validate//", methods=["GET"]) -@fnauth.check_auth(6, False, URL_REDIRECT) +@fnauth.check_auth(6) def validate(token, id_application): """ Call the API to validate a temp user @@ -61,10 +59,9 @@ def validate(token, id_application): # Get temp user infos temp_user = ( - db.session - .query(TempUser.confirmation_url) - .filter(token == TempUser.token_role) - .first() + db.session.query(TempUser.confirmation_url) + .filter(token == TempUser.token_role) + .first() ) if not temp_user: return {"msg": "Aucun utilisateur trouvé avec le token user demandé"}, 404 @@ -81,15 +78,13 @@ def validate(token, id_application): elif not url_after_validation: flash("L'utilisateur a bien été validé") return redirect(url_for("temp_users.temp_users_list")) - + user_data = r.json() # Call post UsersHub actions URL if url_after_validation: r = req_lib.post( - url=url_after_validation, - json=user_data, - cookies=request.cookies + url=url_after_validation, json=user_data, cookies=request.cookies ) if r.status_code == 200: flash("L'utilisateur a bien été validé") @@ -101,7 +96,7 @@ def validate(token, id_application): @routes.route("/delete/", methods=["GET"]) -@fnauth.check_auth(6, False, URL_REDIRECT) +@fnauth.check_auth(6) def delete(token): """ DELETE a temp_user diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index 5e38d17..4ac6c53 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit 5e38d17d992be7a576f216a67bb20c8a8273c6cf +Subproject commit 4ac6c53a554fff050ac0ec9dbb1ca5247ae14da8 From ce97122ac4263ce7ab90c29c24f9d8559f8b2f0a Mon Sep 17 00:00:00 2001 From: TheoLechemia Date: Tue, 10 Oct 2023 12:10:32 +0200 Subject: [PATCH 05/15] bump UH --- dependencies/UsersHub-authentification-module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index 4ac6c53..a686462 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit 4ac6c53a554fff050ac0ec9dbb1ca5247ae14da8 +Subproject commit a6864626abf809078cc8b26824e92b02a39e5bf8 From 29602cdbd18aa503d12a1f35d4d57408a6071983 Mon Sep 17 00:00:00 2001 From: TheoLechemia Date: Mon, 30 Oct 2023 15:36:05 +0100 Subject: [PATCH 06/15] bump UH auth module --- requirements-dependencies.in | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dependencies.in b/requirements-dependencies.in index ba5a3f0..40a6b13 100644 --- a/requirements-dependencies.in +++ b/requirements-dependencies.in @@ -1 +1 @@ -pypnusershub>=1.6.2,<2.0.0 +pypnusershub>=2.0.0,<3 diff --git a/requirements.txt b/requirements.txt index 2e83541..db88592 100644 --- a/requirements.txt +++ b/requirements.txt @@ -108,7 +108,7 @@ pycparser==2.21 # via cffi pyparsing==3.0.9 # via packaging -pypnusershub==1.6.2 +pypnusershub==2.0.0 # via -r requirements-dependencies.in python-dateutil==2.8.2 # via From 8ee7be0724425f3e88056d852fa229a7f3d2b5e1 Mon Sep 17 00:00:00 2001 From: TheoLechemia Date: Wed, 11 Oct 2023 14:53:01 +0200 Subject: [PATCH 07/15] remove after_login_method --- app/app.py | 16 --- dependencies/UsersHub-authentification-module | 2 +- docs/changelog.rst | 10 +- requirements-dependencies.in | 2 +- requirements-dev.txt | 108 ++++++++--------- requirements.txt | 110 ++++++++---------- 6 files changed, 112 insertions(+), 136 deletions(-) diff --git a/app/app.py b/app/app.py index 9bd208f..24ef03b 100644 --- a/app/app.py +++ b/app/app.py @@ -78,22 +78,6 @@ def constants_js(): """Route des constantes javascript""" return render_template("constants.js") - @app.after_request - def after_login_method(response): - """ - Fonction s'exécutant après chaque requete - permet de gérer l'authentification - """ - if not request.cookies.get("token"): - session["current_user"] = None - - if ( - request.endpoint == "auth.login" and response.status_code == 200 - ): # noqa - current_user = json.loads(response.get_data().decode("utf-8")) - session["current_user"] = current_user["user"] - return response - @app.context_processor def inject_user(): return dict(user=getattr(g, "user", None)) diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index a686462..29dcb8b 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit a6864626abf809078cc8b26824e92b02a39e5bf8 +Subproject commit 29dcb8b2e9099c2c21124c8d3666f1fe69ca312d diff --git a/docs/changelog.rst b/docs/changelog.rst index 4f12f17..84b6730 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,7 +2,15 @@ CHANGELOG ========= -2.3.4 (unreleased) +2.4.0 (2023-10-26) +------------------ + +**🚀 Nouveautés** + +* Passage à Flask-Login pour la gestion de l'authentification via la monté de version du sous-module d'authentification en version 2.0.0 + + +2.3.4 (2023-06-27) ------------------ **🐛 Corrections** diff --git a/requirements-dependencies.in b/requirements-dependencies.in index ba5a3f0..a508f2f 100644 --- a/requirements-dependencies.in +++ b/requirements-dependencies.in @@ -1 +1 @@ -pypnusershub>=1.6.2,<2.0.0 +pypnusershub>=2.0.0,<3.0.0 diff --git a/requirements-dev.txt b/requirements-dev.txt index a631463..cb88a2f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,75 +1,69 @@ # -# This file is autogenerated by pip-compile with python 3.7 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile requirements-dev.in # -e file:dependencies/UsersHub-authentification-module#egg=pypnusershub # via -r requirements-submodules.in -alembic==1.8.1 +alembic==1.12.1 # via # flask-migrate # pypnusershub -authlib==1.0.1 +authlib==1.2.1 # via pypnusershub -bcrypt==4.0.0 +bcrypt==4.0.1 # via pypnusershub -certifi==2022.6.15 +blinker==1.6.3 + # via flask +certifi==2023.7.22 # via requests -cffi==1.15.1 +cffi==1.16.0 # via cryptography -charset-normalizer==2.1.1 +charset-normalizer==3.3.1 # via requests -click==8.1.3 +click==8.1.7 # via flask -cryptography==37.0.4 +cryptography==41.0.5 # via authlib -decorator==5.1.1 - # via validators -dnspython==2.2.1 +dnspython==2.4.2 # via email-validator -email-validator==1.2.1 +email-validator==2.1.0.post1 # via wtforms-components -flask==2.2.2 +flask==2.3.3 # via # -r requirements-common.in + # flask-login # flask-marshmallow # flask-migrate # flask-sqlalchemy # flask-wtf # pypnusershub # utils-flask-sqlalchemy -flask-marshmallow==0.14.0 +flask-login==0.6.3 + # via pypnusershub +flask-marshmallow==0.15.0 # via pypnusershub -flask-migrate==3.1.0 +flask-migrate==4.0.5 # via # -r requirements-common.in # utils-flask-sqlalchemy -flask-sqlalchemy==2.5.1 +flask-sqlalchemy==3.0.5 # via # -r requirements-common.in # flask-migrate # pypnusershub # utils-flask-sqlalchemy -flask-wtf==1.0.1 +flask-wtf==1.2.1 # via -r requirements-common.in -greenlet==1.1.3 +greenlet==3.0.1 # via sqlalchemy -gunicorn==20.1.0 +gunicorn==21.2.0 # via -r requirements-common.in -idna==3.3 +idna==3.4 # via # email-validator # requests -importlib-metadata==4.12.0 - # via - # alembic - # click - # flask - # mako - # sqlalchemy -importlib-resources==5.9.0 - # via alembic infinity==1.5 # via intervals intervals==0.9.2 @@ -80,76 +74,74 @@ itsdangerous==2.1.2 # flask-wtf jinja2==3.1.2 # via flask -mako==1.2.2 +mako==1.2.4 # via alembic -markupsafe==2.1.1 +markupsafe==2.1.3 # via # jinja2 # mako # werkzeug # wtforms # wtforms-components -marshmallow==3.17.1 +marshmallow==3.20.1 # via # flask-marshmallow # marshmallow-sqlalchemy # utils-flask-sqlalchemy -marshmallow-sqlalchemy==0.28.1 +marshmallow-sqlalchemy==0.29.0 # via # -r requirements-common.in # pypnusershub -packaging==21.3 +packaging==23.2 # via + # flask-marshmallow + # gunicorn # marshmallow # marshmallow-sqlalchemy -psycopg2==2.9.3 +psycopg2==2.9.9 # via # -r requirements-common.in # pypnusershub pycparser==2.21 # via cffi -pyparsing==3.0.9 - # via packaging python-dateutil==2.8.2 # via # -r requirements-common.in # utils-flask-sqlalchemy -python-dotenv==0.21.0 +python-dotenv==1.0.0 # via -r requirements-common.in -requests==2.28.1 +requests==2.31.0 # via pypnusershub six==1.16.0 # via - # flask-marshmallow # python-dateutil # wtforms-components -sqlalchemy==1.4.40 +sqlalchemy==1.4.50 # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # pypnusershub -typing-extensions==4.3.0 - # via importlib-metadata -urllib3==1.26.12 + # utils-flask-sqlalchemy +typing-extensions==4.8.0 + # via + # alembic + # sqlalchemy +urllib3==2.0.7 # via requests -utils-flask-sqlalchemy==0.3.0 +utils-flask-sqlalchemy==0.3.6 # via pypnusershub -validators==0.20.0 +validators==0.22.0 # via wtforms-components -werkzeug==2.2.2 - # via flask -wtforms==3.0.1 +werkzeug==2.3.7 + # via + # flask + # flask-login + # pypnusershub +wtforms==3.1.0 # via # -r requirements-common.in # flask-wtf # wtforms-components wtforms-components==0.10.5 # via -r requirements-common.in -zipp==3.8.1 - # via - # importlib-metadata - # importlib-resources - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/requirements.txt b/requirements.txt index 2e83541..0e5d856 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,73 +1,67 @@ # -# This file is autogenerated by pip-compile with python 3.7 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile requirements.in # -alembic==1.8.1 +alembic==1.12.1 # via # flask-migrate # pypnusershub -authlib==1.0.1 +authlib==1.2.1 # via pypnusershub -bcrypt==4.0.0 +bcrypt==4.0.1 # via pypnusershub -certifi==2022.6.15 +blinker==1.6.3 + # via flask +certifi==2023.7.22 # via requests -cffi==1.15.1 +cffi==1.16.0 # via cryptography -charset-normalizer==2.1.1 +charset-normalizer==3.3.1 # via requests -click==8.1.3 +click==8.1.7 # via flask -cryptography==37.0.4 +cryptography==41.0.5 # via authlib -decorator==5.1.1 - # via validators -dnspython==2.2.1 +dnspython==2.4.2 # via email-validator -email-validator==1.2.1 +email-validator==2.1.0.post1 # via wtforms-components -flask==2.2.2 +flask==2.3.3 # via # -r requirements-common.in + # flask-login # flask-marshmallow # flask-migrate # flask-sqlalchemy # flask-wtf # pypnusershub # utils-flask-sqlalchemy -flask-marshmallow==0.14.0 +flask-login==0.6.3 + # via pypnusershub +flask-marshmallow==0.15.0 # via pypnusershub -flask-migrate==3.1.0 +flask-migrate==4.0.5 # via # -r requirements-common.in # utils-flask-sqlalchemy -flask-sqlalchemy==2.5.1 +flask-sqlalchemy==3.0.5 # via # -r requirements-common.in # flask-migrate # pypnusershub # utils-flask-sqlalchemy -flask-wtf==1.0.1 +flask-wtf==1.2.1 # via -r requirements-common.in -greenlet==1.1.3 +greenlet==3.0.1 # via sqlalchemy -gunicorn==20.1.0 +gunicorn==21.2.0 # via -r requirements-common.in -idna==3.3 +idna==3.4 # via # email-validator # requests -importlib-metadata==4.12.0 - # via - # alembic - # click - # flask - # mako - # sqlalchemy -importlib-resources==5.9.0 - # via alembic infinity==1.5 # via intervals intervals==0.9.2 @@ -78,78 +72,76 @@ itsdangerous==2.1.2 # flask-wtf jinja2==3.1.2 # via flask -mako==1.2.2 +mako==1.2.4 # via alembic -markupsafe==2.1.1 +markupsafe==2.1.3 # via # jinja2 # mako # werkzeug # wtforms # wtforms-components -marshmallow==3.17.1 +marshmallow==3.20.1 # via # flask-marshmallow # marshmallow-sqlalchemy # utils-flask-sqlalchemy -marshmallow-sqlalchemy==0.28.1 +marshmallow-sqlalchemy==0.29.0 # via # -r requirements-common.in # pypnusershub -packaging==21.3 +packaging==23.2 # via + # flask-marshmallow + # gunicorn # marshmallow # marshmallow-sqlalchemy -psycopg2==2.9.3 +psycopg2==2.9.9 # via # -r requirements-common.in # pypnusershub pycparser==2.21 # via cffi -pyparsing==3.0.9 - # via packaging -pypnusershub==1.6.2 +pypnusershub==2.0.0 # via -r requirements-dependencies.in python-dateutil==2.8.2 # via # -r requirements-common.in # utils-flask-sqlalchemy -python-dotenv==0.21.0 +python-dotenv==1.0.0 # via -r requirements-common.in -requests==2.28.1 +requests==2.31.0 # via pypnusershub six==1.16.0 # via - # flask-marshmallow # python-dateutil # wtforms-components -sqlalchemy==1.4.40 +sqlalchemy==1.4.50 # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # pypnusershub -typing-extensions==4.3.0 - # via importlib-metadata -urllib3==1.26.12 + # utils-flask-sqlalchemy +typing-extensions==4.8.0 + # via + # alembic + # sqlalchemy +urllib3==2.0.7 # via requests -utils-flask-sqlalchemy==0.3.0 +utils-flask-sqlalchemy==0.3.6 # via pypnusershub -validators==0.20.0 +validators==0.22.0 # via wtforms-components -werkzeug==2.2.2 - # via flask -wtforms==3.0.1 +werkzeug==2.3.7 + # via + # flask + # flask-login + # pypnusershub +wtforms==3.1.0 # via # -r requirements-common.in # flask-wtf # wtforms-components wtforms-components==0.10.5 # via -r requirements-common.in -zipp==3.8.1 - # via - # importlib-metadata - # importlib-resources - -# The following packages are considered to be unsafe in a requirements file: -# setuptools From 131c62087335225bbcdb945931f4723d474f86e1 Mon Sep 17 00:00:00 2001 From: Jacques Fize <4259846+jacquesfize@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:03:31 +0100 Subject: [PATCH 08/15] fix breaking change (caused by flask_login) on Geonature password change route (#191) fix breaking change provoked by flask_login on the password change route --- app/api/route_register.py | 11 +++++++---- app/app.py | 16 ++++++++++++++-- app/utils/errors.py | 23 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 app/utils/errors.py diff --git a/app/api/route_register.py b/app/api/route_register.py index 04106fe..f04cbff 100644 --- a/app/api/route_register.py +++ b/app/api/route_register.py @@ -122,12 +122,15 @@ def valid_temp_user(): # recherche de l'utilisateur temporaire correspondant au token temp_user = db.session.query(TempUser).filter(token == TempUser.token_role).first() if not temp_user: - return { - "msg": f""" + return ( + { + "msg": f""" Il n'y a pas d'utilisateur temporaire correspondant au token fourni {token}.
Il se peut que la demande de création de compte ai déjà été validée, ou bien que l'adresse de validation soit erronée.
""" - }, 422 + }, + 422, + ) req_data = temp_user.as_dict() # Récupération du groupe par défaut @@ -167,6 +170,7 @@ def set_cor_role_token(email): Fonction pour la création d'un token associé a un id_role Parametres : email """ + if not email: return {"msg": "Aucun email"}, 404 @@ -220,7 +224,6 @@ def create_cor_role_token(): data = request.get_json() email = data["email"] - return set_cor_role_token(email) diff --git a/app/app.py b/app/app.py index 24ef03b..99352e4 100644 --- a/app/app.py +++ b/app/app.py @@ -7,10 +7,19 @@ import json import logging from pkg_resources import iter_entry_points -from urllib.parse import urlsplit +from urllib.parse import urlsplit, urlencode from pathlib import Path -from flask import Flask, redirect, url_for, request, session, render_template, g +from flask import ( + Flask, + Response, + redirect, + url_for, + request, + session, + render_template, + g, +) from werkzeug.middleware.proxy_fix import ProxyFix from sqlalchemy.exc import ProgrammingError from flask_migrate import Migrate @@ -19,6 +28,7 @@ from pypnusershub.db.models import Application from pypnusershub.login_manager import login_manager +from app.utils.errors import handle_unauthenticated_request migrate = Migrate() @@ -129,4 +139,6 @@ def inject_user(): route_register.route, url_prefix="/api_register" ) # noqa + app.login_manager.unauthorized_handler(handle_unauthenticated_request) + return app diff --git a/app/utils/errors.py b/app/utils/errors.py new file mode 100644 index 0000000..93f0bde --- /dev/null +++ b/app/utils/errors.py @@ -0,0 +1,23 @@ +from flask import current_app, Response, request, redirect, url_for +from urllib.parse import urlencode +from werkzeug.exceptions import Unauthorized + + +# Unauthorized means disconnected +# (logged but not allowed to perform an action = Forbidden) + + +def handle_unauthenticated_request(): + """ + To avoid returning the login page html when a route is used by geonature API + this function overrides `LoginManager.unauthorized()` from `flask-login` . + + Returns + ------- + flask.Response + response + """ + if "application/json" in request.headers.get("Content-Type", ""): + raise Unauthorized + else: + return redirect(url_for("login.login", next=request.path)) From f9e452b4f18d84e8aa79da36271a811688688ab1 Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Fri, 26 Jan 2024 13:40:12 +0100 Subject: [PATCH 09/15] add lint ci + lint to black 24 --- .github/workflows/lint.yml | 13 ++ app/api/route_register.py | 1 + app/bib_organismes/forms.py | 40 ++++-- app/env.py | 2 +- app/genericRepository.py | 6 - app/groupe/forms.py | 17 ++- app/liste/forms.py | 14 +- app/migrations/env.py | 22 +-- ...ec215fe023e_upgrade_utilisateurs_schema.py | 9 +- .../versions/9445a69f2bed_usershub.py | 27 ++-- .../versions/f63a8f44c969_usershub_samples.py | 21 +-- app/models.py | 17 +-- app/t_profils/forms.py | 14 +- app/t_roles/forms.py | 64 +++++---- app/t_roles/route.py | 7 +- app/utils/utilssqlalchemy.py | 134 ++++++++---------- 16 files changed, 223 insertions(+), 185 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..20c6ab4 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,13 @@ +name: Lint + +on: [push, pull_request] + +jobs: + backend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Backend code formatting check (Black) + uses: psf/black@stable diff --git a/app/api/route_register.py b/app/api/route_register.py index f04cbff..15c3d71 100644 --- a/app/api/route_register.py +++ b/app/api/route_register.py @@ -1,6 +1,7 @@ """ Route permettant de manipuler les données de UsersHub via une API """ + from datetime import datetime, timedelta import hashlib diff --git a/app/bib_organismes/forms.py b/app/bib_organismes/forms.py index cfc2609..702761c 100644 --- a/app/bib_organismes/forms.py +++ b/app/bib_organismes/forms.py @@ -1,25 +1,37 @@ from flask_wtf import FlaskForm -from wtforms import (StringField, PasswordField, BooleanField, - SubmitField, HiddenField, SelectField, validators +from wtforms import ( + StringField, + PasswordField, + BooleanField, + SubmitField, + HiddenField, + SelectField, + validators, ) from wtforms.validators import DataRequired, Email -# from wtforms_components import IntergerField - +# from wtforms_components import IntergerField class Organisme(FlaskForm): """ Classe du formulaire des Organismes """ - nom_organisme = StringField("Nom de l'organisme", validators=[DataRequired(message="Le nom de l'organisme est obligatoire")]) - adresse_organisme = StringField('Adresse') - cp_organisme = StringField('Code Postal') - ville_organisme = StringField ('Ville') - tel_organisme = StringField('Téléphone') - fax_organisme = StringField('Fax') - email_organisme = StringField('E-mail', validators=[validators.Optional(), Email(message="L'email est incorect")]) + + nom_organisme = StringField( + "Nom de l'organisme", + validators=[DataRequired(message="Le nom de l'organisme est obligatoire")], + ) + adresse_organisme = StringField("Adresse") + cp_organisme = StringField("Code Postal") + ville_organisme = StringField("Ville") + tel_organisme = StringField("Téléphone") + fax_organisme = StringField("Fax") + email_organisme = StringField( + "E-mail", + validators=[validators.Optional(), Email(message="L'email est incorect")], + ) url_organisme = StringField("URL du site web de l'organisme") - url_logo = StringField('Logo (URL)') - id_organisme = HiddenField('id') - submit = SubmitField('Enregistrer') + url_logo = StringField("Logo (URL)") + id_organisme = HiddenField("id") + submit = SubmitField("Enregistrer") diff --git a/app/env.py b/app/env.py index 2fe448c..b639f04 100644 --- a/app/env.py +++ b/app/env.py @@ -6,5 +6,5 @@ Création de la base avec sqlalchemy """ -os.environ['FLASK_SQLALCHEMY_DB'] = 'app.env.db' +os.environ["FLASK_SQLALCHEMY_DB"] = "app.env.db" db = SQLAlchemy() diff --git a/app/genericRepository.py b/app/genericRepository.py index ab59e60..c931f0c 100644 --- a/app/genericRepository.py +++ b/app/genericRepository.py @@ -36,7 +36,6 @@ def get_all( order_by=None, order="asc", ): - """ Methode qui retourne un dictionnaire de tout les éléments d'un Model Avec pour paramètres: @@ -65,7 +64,6 @@ def get_all( @classmethod def post(cls, entity_dict): - """ Methode qui ajoute un élément à une table Avec pour paramètres un dictionnaire de cet élément @@ -83,7 +81,6 @@ def post(cls, entity_dict): @classmethod def update(cls, entity_dict): - """ Methode qui met à jour un élément Avec pour paramètre un dictionnaire de cet élément @@ -101,7 +98,6 @@ def update(cls, entity_dict): @classmethod def delete(cls, id): - """ Methode qui supprime un élement d'une table à partir d'un id donné Avec pour paramètre un id (clé primaire) @@ -115,7 +111,6 @@ def delete(cls, id): @classmethod def choixSelect(cls, id, nom, aucun=None, order_by=None): - """ Methode qui retourne un tableau de tuples d'id et de nom Avec pour paramètres un id et un nom @@ -135,4 +130,3 @@ def choixSelect(cls, id, nom, aucun=None, order_by=None): # for col in cls.__table__.columns.keys() # return cls.__table__.columns.keys() - diff --git a/app/groupe/forms.py b/app/groupe/forms.py index 574fd9e..ccbba84 100644 --- a/app/groupe/forms.py +++ b/app/groupe/forms.py @@ -4,12 +4,17 @@ class Group(FlaskForm): - """ Classe du formulaire des Groupes """ - nom_role = StringField("Nom", validators=[DataRequired(message='Le nom du group est obligatoire')]) - desc_role = TextAreaField('Description') - groupe = BooleanField('groupe', validators=[DataRequired(message="L'information 'groupe' est obligatoire")]) - id_role = HiddenField('id') - submit = SubmitField('Enregistrer') \ No newline at end of file + + nom_role = StringField( + "Nom", validators=[DataRequired(message="Le nom du group est obligatoire")] + ) + desc_role = TextAreaField("Description") + groupe = BooleanField( + "groupe", + validators=[DataRequired(message="L'information 'groupe' est obligatoire")], + ) + id_role = HiddenField("id") + submit = SubmitField("Enregistrer") diff --git a/app/liste/forms.py b/app/liste/forms.py index a5cb6e3..8e5cc98 100644 --- a/app/liste/forms.py +++ b/app/liste/forms.py @@ -8,8 +8,12 @@ class List(FlaskForm): Classe du formulaire des listes """ - nom_liste = StringField("Nom", validators = [DataRequired(message = 'Le nom de la liste est obligatoire')]) - code_liste = StringField("Code", validators = [DataRequired(message = 'Le code de la liste est obligatoire')]) - desc_liste = TextAreaField('Description') - id_liste = HiddenField('Id') - submit = SubmitField('Enregistrer') \ No newline at end of file + nom_liste = StringField( + "Nom", validators=[DataRequired(message="Le nom de la liste est obligatoire")] + ) + code_liste = StringField( + "Code", validators=[DataRequired(message="Le code de la liste est obligatoire")] + ) + desc_liste = TextAreaField("Description") + id_liste = HiddenField("Id") + submit = SubmitField("Enregistrer") diff --git a/app/migrations/env.py b/app/migrations/env.py index 9452179..b896f2c 100644 --- a/app/migrations/env.py +++ b/app/migrations/env.py @@ -15,17 +15,19 @@ # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) -logger = logging.getLogger('alembic.env') +logger = logging.getLogger("alembic.env") # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata from flask import current_app + config.set_main_option( - 'sqlalchemy.url', - str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) -target_metadata = current_app.extensions['migrate'].db.metadata + "sqlalchemy.url", + str(current_app.extensions["migrate"].db.engine.url).replace("%", "%%"), +) +target_metadata = current_app.extensions["migrate"].db.metadata # other values from the config, defined by the needs of env.py, # can be acquired: @@ -46,9 +48,7 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, target_metadata=target_metadata, literal_binds=True - ) + context.configure(url=url, target_metadata=target_metadata, literal_binds=True) with context.begin_transaction(): context.run_migrations() @@ -66,15 +66,15 @@ def run_migrations_online(): # when there are no changes to the schema # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, 'autogenerate', False): + if getattr(config.cmd_opts, "autogenerate", False): script = directives[0] if script.upgrade_ops.is_empty(): directives[:] = [] - logger.info('No changes in schema detected.') + logger.info("No changes in schema detected.") connectable = engine_from_config( config.get_section(config.config_ini_section), - prefix='sqlalchemy.', + prefix="sqlalchemy.", poolclass=pool.NullPool, ) @@ -83,7 +83,7 @@ def process_revision_directives(context, revision, directives): connection=connection, target_metadata=target_metadata, process_revision_directives=process_revision_directives, - **current_app.extensions['migrate'].configure_args + **current_app.extensions["migrate"].configure_args ) with context.begin_transaction(): diff --git a/app/migrations/versions/6ec215fe023e_upgrade_utilisateurs_schema.py b/app/migrations/versions/6ec215fe023e_upgrade_utilisateurs_schema.py index 9ceb889..a36642b 100644 --- a/app/migrations/versions/6ec215fe023e_upgrade_utilisateurs_schema.py +++ b/app/migrations/versions/6ec215fe023e_upgrade_utilisateurs_schema.py @@ -5,17 +5,16 @@ Create Date: 2021-09-30 16:29:25.531376 """ + from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '6ec215fe023e' -down_revision = '9445a69f2bed' +revision = "6ec215fe023e" +down_revision = "9445a69f2bed" branch_labels = None -depends_on = ( - '951b8270a1cf', # utilisateurs -) +depends_on = ("951b8270a1cf",) # utilisateurs def upgrade(): diff --git a/app/migrations/versions/9445a69f2bed_usershub.py b/app/migrations/versions/9445a69f2bed_usershub.py index 278c62c..73aa9e1 100644 --- a/app/migrations/versions/9445a69f2bed_usershub.py +++ b/app/migrations/versions/9445a69f2bed_usershub.py @@ -4,21 +4,21 @@ Create Date: 2021-08-30 16:33:42.410504 """ + from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '9445a69f2bed' +revision = "9445a69f2bed" down_revision = None -branch_labels = ('usershub',) -depends_on = ( - 'fa35dfe5ff27', # schema utilisateurs -) +branch_labels = ("usershub",) +depends_on = ("fa35dfe5ff27",) # schema utilisateurs def upgrade(): - op.execute(""" + op.execute( + """ INSERT INTO utilisateurs.t_applications ( code_application, nom_application, @@ -29,8 +29,10 @@ def upgrade(): 'UsersHub', 'Application permettant d''administrer la présente base de données.', NULL) - """) - op.execute(""" + """ + ) + op.execute( + """ INSERT INTO utilisateurs.cor_profil_for_app (id_profil, id_application) VALUES @@ -41,14 +43,17 @@ def upgrade(): (SELECT id_profil FROM utilisateurs.t_profils WHERE code_profil = '3'), (SELECT id_application FROM utilisateurs.t_applications WHERE code_application = 'UH') ) - """) + """ + ) def downgrade(): - op.execute(""" + op.execute( + """ DELETE FROM utilisateurs.cor_profil_for_app cor USING utilisateurs.t_applications app WHERE cor.id_application = app.id_application AND app.code_application = 'UH' - """) + """ + ) op.execute("DELETE FROM utilisateurs.t_applications WHERE code_application = 'UH'") diff --git a/app/migrations/versions/f63a8f44c969_usershub_samples.py b/app/migrations/versions/f63a8f44c969_usershub_samples.py index 14ec094..c2cdf54 100644 --- a/app/migrations/versions/f63a8f44c969_usershub_samples.py +++ b/app/migrations/versions/f63a8f44c969_usershub_samples.py @@ -5,33 +5,37 @@ Create Date: 2021-09-06 18:17:06.392398 """ + from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'f63a8f44c969' +revision = "f63a8f44c969" down_revision = None -branch_labels = ('usershub-samples',) +branch_labels = ("usershub-samples",) depends_on = ( - '9445a69f2bed', # usershub - '72f227e37bdf', # utilisateurs schema samples data + "9445a69f2bed", # usershub + "72f227e37bdf", # utilisateurs schema samples data ) def upgrade(): - op.execute(""" + op.execute( + """ INSERT INTO utilisateurs.cor_role_app_profil (id_role, id_application, id_profil) VALUES ( (SELECT id_role FROM utilisateurs.t_roles WHERE nom_role = 'Grp_admin'), (SELECT id_application FROM utilisateurs.t_applications WHERE code_application = 'UH'), (SELECT id_profil FROM utilisateurs.t_profils WHERE code_profil = '6') ) - """) + """ + ) def downgrade(): - op.execute(""" + op.execute( + """ DELETE FROM utilisateurs.cor_role_app_profil cor USING utilisateurs.t_roles r, @@ -44,4 +48,5 @@ def downgrade(): AND r.nom_role = 'Grp_admin' AND a.code_application = 'UH' AND p.code_profil = '6' - """) + """ + ) diff --git a/app/models.py b/app/models.py index e867b2f..a335bba 100644 --- a/app/models.py +++ b/app/models.py @@ -194,7 +194,7 @@ def get_full_name(self): Methode qui concatène le nom et prénom du role retourne un nom complet """ - return ' '.join([(self.nom_role or ''), (self.prenom_role or '')]) + return " ".join([(self.nom_role or ""), (self.prenom_role or "")]) def as_dict_full_name(self): """ @@ -298,7 +298,6 @@ def get_user_out_group(cls, id_groupe): @classmethod def get_user_profil_in_app(cls, id_application): - """ Methode qui retourne un dictionnaire de roles avec leur profil sur une application Avec pour paramètre un id d'application @@ -324,7 +323,6 @@ def get_user_profil_in_app(cls, id_application): @classmethod def get_user_profil_out_app(cls, id_application): - """ Methode qui retourne un dictionnaire de roles n'ayant pas de droits sur une application @@ -341,7 +339,6 @@ def get_user_profil_out_app(cls, id_application): @classmethod def get_groups(cls): - """ Methode qui retourne une liste des roles de type groupe @@ -352,7 +349,6 @@ def get_groups(cls): @serializable class CorRoles(GenericRepository): - """ Classe de correspondance entre un utilisateur et un groupe """ @@ -383,7 +379,6 @@ def test_role_in_grp(cls, id_role, id_group): @classmethod def add_cor(cls, id_group, ids_role): - """ Methode qui ajoute des relations roles <-> groupe Avec pour paramètres un id de groupe(id_role) @@ -398,7 +393,6 @@ def add_cor(cls, id_group, ids_role): @classmethod def del_cor(cls, id_group, ids_role): - """ Methode qui supprime des relations roles <-> groupe Avec pour paramètres un id de groupe(id_role) et un tableau @@ -414,7 +408,6 @@ def del_cor(cls, id_group, ids_role): @serializable class TListes(GenericRepository): - """ Model de la table t_listes """ @@ -429,7 +422,7 @@ class TListes(GenericRepository): @serializable class CorRoleListe(GenericRepository): - """ Classe de correspondance entre la table t_roles et la table t_listes""" + """Classe de correspondance entre la table t_roles et la table t_listes""" __tablename__ = "cor_role_liste" __table_args__ = {"schema": "utilisateurs", "extend_existing": True} @@ -470,7 +463,6 @@ def del_cor(cls, id_liste, ids_role): @serializable class TApplications(GenericRepository): - """ Model de la table t_applications """ @@ -517,7 +509,7 @@ def get_profils_in_app(cls, id_application): @classmethod def get_profil_in_app_with_code(cls, id_application, code_profil): """ - Methode qui retourne un profil à partir de son code + Methode qui retourne un profil à partir de son code """ return ( db.session.query(TProfils) @@ -560,7 +552,7 @@ def choixSelect( @serializable class CorProfilForApp(GenericRepository): - """ Classe de correspondance entre la table t_applications et la table t_profils""" + """Classe de correspondance entre la table t_applications et la table t_profils""" __tablename__ = "cor_profil_for_app" __table_args__ = {"schema": "utilisateurs", "extend_existing": True} @@ -689,4 +681,3 @@ def del_cor(cls, id_app, tab_profil): cls.id_profil == t["id_profil"] ).filter(cls.id_application == id_app).delete() db.session.commit() - diff --git a/app/t_profils/forms.py b/app/t_profils/forms.py index 21475de..887c058 100644 --- a/app/t_profils/forms.py +++ b/app/t_profils/forms.py @@ -7,9 +7,13 @@ class Profil(FlaskForm): """ Classe du formulaire des profils """ - id_profil = HiddenField('Id') - nom_profil = StringField('Nom', validators = [DataRequired(message = 'Le nom du profil est obligatoire')]) - code_profil = StringField('Code', validators = [DataRequired(message = 'Le code du profil est obligatoire')]) - desc_profil = StringField('Description') - submit = SubmitField('Enregistrer') + id_profil = HiddenField("Id") + nom_profil = StringField( + "Nom", validators=[DataRequired(message="Le nom du profil est obligatoire")] + ) + code_profil = StringField( + "Code", validators=[DataRequired(message="Le code du profil est obligatoire")] + ) + desc_profil = StringField("Description") + submit = SubmitField("Enregistrer") diff --git a/app/t_roles/forms.py b/app/t_roles/forms.py index dec87f9..19adb4f 100644 --- a/app/t_roles/forms.py +++ b/app/t_roles/forms.py @@ -1,12 +1,20 @@ -''' +""" Définition du formulaire : création/modification d'un role -''' +""" from flask_wtf import FlaskForm from wtforms import ( - StringField, PasswordField, SubmitField, HiddenField, SelectField, - RadioField, BooleanField, SelectMultipleField, TextAreaField, widgets, - validators + StringField, + PasswordField, + SubmitField, + HiddenField, + SelectField, + RadioField, + BooleanField, + SelectMultipleField, + TextAreaField, + widgets, + validators, ) from wtforms.validators import DataRequired, Email @@ -17,26 +25,30 @@ class MultiCheckboxField(SelectMultipleField): class Utilisateur(FlaskForm): - active = BooleanField('Actif', default = True, false_values=(False, 'false')) - nom_role = StringField('Nom', validators=[DataRequired(message="Le nom de l'utilisateur est obligatoire")]) - prenom_role = StringField('Prenom') - desc_role = TextAreaField('Description') - id_organisme = SelectField('Organisme', coerce=int, choices=[], default=-1) - a_groupe = SelectMultipleField('', choices=[], coerce=int) - identifiant = StringField('Identifiant') - pass_plus = PasswordField('Mot de passe') - mdpconf = PasswordField('Confirmation') - email = StringField('E-mail', validators=[validators.Optional(), Email(message="L'email est incorect")]) - groupe = HiddenField('groupe', default=None) - remarques = TextAreaField('Commentaire') - id_role = HiddenField('id') - submit = SubmitField('Enregistrer') - -class UserPass(FlaskForm): - pass_plus = PasswordField('Mot de passe') - mdpconf = PasswordField('Confirmation') - id_role = HiddenField('id') - submit = SubmitField('Enregistrer') - + active = BooleanField("Actif", default=True, false_values=(False, "false")) + nom_role = StringField( + "Nom", + validators=[DataRequired(message="Le nom de l'utilisateur est obligatoire")], + ) + prenom_role = StringField("Prenom") + desc_role = TextAreaField("Description") + id_organisme = SelectField("Organisme", coerce=int, choices=[], default=-1) + a_groupe = SelectMultipleField("", choices=[], coerce=int) + identifiant = StringField("Identifiant") + pass_plus = PasswordField("Mot de passe") + mdpconf = PasswordField("Confirmation") + email = StringField( + "E-mail", + validators=[validators.Optional(), Email(message="L'email est incorect")], + ) + groupe = HiddenField("groupe", default=None) + remarques = TextAreaField("Commentaire") + id_role = HiddenField("id") + submit = SubmitField("Enregistrer") +class UserPass(FlaskForm): + pass_plus = PasswordField("Mot de passe") + mdpconf = PasswordField("Confirmation") + id_role = HiddenField("id") + submit = SubmitField("Enregistrer") diff --git a/app/t_roles/route.py b/app/t_roles/route.py index ac21ed0..bf0a315 100644 --- a/app/t_roles/route.py +++ b/app/t_roles/route.py @@ -208,8 +208,8 @@ def updatepass(id_role=None): form = t_rolesforms.UserPass() myuser = TRoles.get_one(id_role) # Build title - role_fullname= buildUserFullName(myuser) - title=f"Changer le mot de passe de l'utilisateur '{role_fullname}'" + role_fullname = buildUserFullName(myuser) + title = f"Changer le mot de passe de l'utilisateur '{role_fullname}'" if request.method == "POST": if form.validate_on_submit() and form.validate(): @@ -282,13 +282,14 @@ def info(id_role): pathU=URL_APPLICATION + "/user/update/", ) + def buildUserFullName(user): fullname = [] if user["nom_role"]: fullname.append(user["nom_role"].upper()) if user["prenom_role"]: fullname.append(user["prenom_role"].title()) - return ' '.join(fullname) + return " ".join(fullname) def pops(form, with_group=True): diff --git a/app/utils/utilssqlalchemy.py b/app/utils/utilssqlalchemy.py index 043b11a..df6dcf6 100644 --- a/app/utils/utilssqlalchemy.py +++ b/app/utils/utilssqlalchemy.py @@ -1,6 +1,7 @@ -''' +""" Fonctions utilitaires -''' +""" + import json from functools import wraps @@ -13,7 +14,6 @@ from sqlalchemy.orm import ColumnProperty - # def testDataType(value, sqlType, paramName): # if sqlType == DB.Integer or isinstance(sqlType, (DB.Integer)): # try: @@ -39,19 +39,20 @@ MANQUE FLOAT """ SERIALIZERS = { - 'date': lambda x: str(x) if x else None, - 'datetime': lambda x: str(x) if x else None, - 'time': lambda x: str(x) if x else None, - 'timestamp': lambda x: str(x) if x else None, - 'uuid': lambda x: str(x) if x else None + "date": lambda x: str(x) if x else None, + "datetime": lambda x: str(x) if x else None, + "time": lambda x: str(x) if x else None, + "timestamp": lambda x: str(x) if x else None, + "uuid": lambda x: str(x) if x else None, } class GenericTable: """ - Classe permettant de créer à la volée un mapping - d'une vue avec la base de données par rétroingénierie + Classe permettant de créer à la volée un mapping + d'une vue avec la base de données par rétroingénierie """ + def __init__(self, tableName, schemaName, geometry_field): meta = MetaData(schema=schemaName, bind=DB.engine) meta.reflect(views=True) @@ -64,29 +65,27 @@ def __init__(self, tableName, schemaName, geometry_field): # Mise en place d'un mapping des colonnes en vue d'une sérialisation self.serialize_columns = [ - ( - name, - SERIALIZERS.get( - db_col.type.__class__.__name__.lower(), - lambda x: x - ) - ) + (name, SERIALIZERS.get(db_col.type.__class__.__name__.lower(), lambda x: x)) for name, db_col in self.tableDef.columns.items() - if not db_col.type.__class__.__name__ == 'Geometry' + if not db_col.type.__class__.__name__ == "Geometry" ] self.columns = [column.name for column in self.tableDef.columns] def as_dict(self, data): return { - item: _serializer(getattr(data, item)) for item, _serializer in self.serialize_columns + item: _serializer(getattr(data, item)) + for item, _serializer in self.serialize_columns } + def serializeQuery(data, columnDef): rows = [ { - c['name']: getattr(row, c['name']) - for c in columnDef if getattr(row, c['name']) is not None - } for row in data + c["name"]: getattr(row, c["name"]) + for c in columnDef + if getattr(row, c["name"]) is not None + } + for row in data ] return rows @@ -96,29 +95,30 @@ def serializeQueryTest(data, columnDef): for row in data: inter = {} for c in columnDef: - if getattr(row, c['name']) is not None: - if isinstance(c['type'], (DB.Date, DB.DateTime, UUID)): - inter[c['name']] = str(getattr(row, c['name'])) - elif isinstance(c['type'], DB.Numeric): - inter[c['name']] = float(getattr(row, c['name'])) - elif not isinstance(c['type'], Geometry): - inter[c['name']] = getattr(row, c['name']) + if getattr(row, c["name"]) is not None: + if isinstance(c["type"], (DB.Date, DB.DateTime, UUID)): + inter[c["name"]] = str(getattr(row, c["name"])) + elif isinstance(c["type"], DB.Numeric): + inter[c["name"]] = float(getattr(row, c["name"])) + elif not isinstance(c["type"], Geometry): + inter[c["name"]] = getattr(row, c["name"]) rows.append(inter) return rows def serializeQueryOneResult(row, columnDef): row = { - c['name']: getattr(row, c['name']) - for c in columnDef if getattr(row, c['name']) is not None + c["name"]: getattr(row, c["name"]) + for c in columnDef + if getattr(row, c["name"]) is not None } return row def serializable(cls): """ - Décorateur de classe pour les DB.Models - Permet de rajouter la fonction as_dict qui est basée sur le mapping SQLAlchemy + Décorateur de classe pour les DB.Models + Permet de rajouter la fonction as_dict qui est basée sur le mapping SQLAlchemy """ """ @@ -131,15 +131,16 @@ def serializable(cls): db_col = prop.columns[0] # HACK # -> Récupération du nom de l'attribut sans la classe - name = str(prop).split('.', 1)[1] - if not db_col.type.__class__.__name__ == 'Geometry': - cls_db_columns.append(( - name, - SERIALIZERS.get( - db_col.type.__class__.__name__.lower(), - lambda x: x + name = str(prop).split(".", 1)[1] + if not db_col.type.__class__.__name__ == "Geometry": + cls_db_columns.append( + ( + name, + SERIALIZERS.get( + db_col.type.__class__.__name__.lower(), lambda x: x + ), ) - )) + ) """ Liste des propriétés synonymes @@ -149,17 +150,13 @@ def serializable(cls): for syn in cls.__mapper__.synonyms: col = cls.__mapper__.c[syn.name] # if column type is geometry pass - if col.type.__class__.__name__ == 'Geometry': + if col.type.__class__.__name__ == "Geometry": pass # else add synonyms in columns properties - cls_db_columns.append(( - syn.key, - SERIALIZERS.get( - col.type.__class__.__name__.lower(), - lambda x: x - ) - )) + cls_db_columns.append( + (syn.key, SERIALIZERS.get(col.type.__class__.__name__.lower(), lambda x: x)) + ) """ Liste des propriétés de type relationship @@ -187,13 +184,11 @@ def serializefn(self, recursif=False, columns=()): else: fprops = cls_db_columns - out = { - item: _serializer(getattr(self, item)) for item, _serializer in fprops - } + out = {item: _serializer(getattr(self, item)) for item, _serializer in fprops} if recursif is False: return out - for (rel, uselist) in cls_db_relationships: + for rel, uselist in cls_db_relationships: if getattr(self, rel) is None: break @@ -209,10 +204,11 @@ def serializefn(self, recursif=False, columns=()): def json_resp(fn): - ''' + """ Décorateur transformant le résultat renvoyé par une vue en objet JSON - ''' + """ + @wraps(fn) def _json_resp(*args, **kwargs): res = fn(*args, **kwargs) @@ -223,20 +219,18 @@ def _json_resp(*args, **kwargs): if not res: status = 404 - res = {'message': 'not found'} + res = {"message": "not found"} + + return Response(json.dumps(res), status=status, mimetype="application/json") - return Response( - json.dumps(res), - status=status, - mimetype='application/json' - ) return _json_resp def csv_resp(fn): - ''' + """ Décorateur transformant le résultat renvoyé en un fichier csv - ''' + """ + @wraps(fn) def _csv_resp(*args, **kwargs): res = fn(*args, **kwargs) @@ -244,20 +238,18 @@ def _csv_resp(*args, **kwargs): outdata = [separator.join(columns)] headers = Headers() - headers.add('Content-Type', 'text/plain') + headers.add("Content-Type", "text/plain") headers.add( - 'Content-Disposition', - 'attachment', - filename='export_%s.csv' % filename + "Content-Disposition", "attachment", filename="export_%s.csv" % filename ) for o in data: outdata.append( separator.join( - '"%s"' % (o.get(i), '') - [o.get(i) is None] for i in columns + '"%s"' % (o.get(i), "")[o.get(i) is None] for i in columns ) ) - out = '\r\n'.join(outdata) + out = "\r\n".join(outdata) return Response(out, headers=headers) - return _csv_resp \ No newline at end of file + + return _csv_resp From 8b35a30fc00b13a3edf3efecf4292795c290394a Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Fri, 26 Jan 2024 13:41:56 +0100 Subject: [PATCH 10/15] lint --- setup.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index ea7ee84..7677e24 100644 --- a/setup.py +++ b/setup.py @@ -3,22 +3,22 @@ root_dir = Path(__file__).absolute().parent -with (root_dir / 'VERSION').open() as f: +with (root_dir / "VERSION").open() as f: version = f.read() -with (root_dir / 'README.rst').open() as f: +with (root_dir / "README.rst").open() as f: long_description = f.read() setuptools.setup( - name='usershub', - description='Application web de gestion centralisée des utilisateurs', + name="usershub", + description="Application web de gestion centralisée des utilisateurs", long_description=long_description, - long_description_content_type='text/x-rst', - maintainer='Parcs nationaux des Écrins et des Cévennes', - maintainer_email='geonature@ecrins-parcnational.fr', - url='https://github.com/PnX-SI/UsersHub', + long_description_content_type="text/x-rst", + maintainer="Parcs nationaux des Écrins et des Cévennes", + maintainer_email="geonature@ecrins-parcnational.fr", + url="https://github.com/PnX-SI/UsersHub", version=version, - packages=setuptools.find_packages(where='.', include=['app*']), + packages=setuptools.find_packages(where=".", include=["app*"]), install_requires=( list(open("requirements-common.in", "r")) + list(open("requirements-dependencies.in", "r")) From f4c23dfc711592528deeb28f50349dfbd48f58b3 Mon Sep 17 00:00:00 2001 From: Camille Monchicourt Date: Fri, 26 Jan 2024 23:06:28 +0100 Subject: [PATCH 11/15] Changelog 2.4.0 --- docs/changelog.rst | 63 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f3cc894..c3ac140 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,12 +2,15 @@ CHANGELOG ========= -2.4.0 (2023-10-26) +2.4.0 (unreleased) ------------------ **🚀 Nouveautés** -* Passage à Flask-Login pour la gestion de l'authentification via la monté de version du sous-module d'authentification en version 2.0.0 +- Passage à Flask-Login pour la gestion de l'authentification via la mise à jour de UsersHub-authentification-module en version 2.0.0 (#188) +- Mise à jour de nombreuses dépendances Python +- Mise à jour du linter black en version 24 +- Rechargement automatique du Docker quand on modifie le fichier de configuration de UsersHub 2.3.4 (2023-06-27) @@ -15,24 +18,24 @@ CHANGELOG **🐛 Corrections** -* Correction de la route de redirection après login ou un logout pour éviter les erreurs de redirection lorsque l'URL de l'application n'est pas située à la racine (#173 / #174 - par @joelclems) -* Création de compte : affichage d'un message explicite lorsque le token d'un utlisateur temporaire n'est pas trouvé quand un administrateur cherche à le valider (#177 / https://github.com/PnX-SI/GeoNature/issues/862 - par @joelclems) -* Correction de l'affichage de la fiche d'un utilisateur si celui-ci n'a pas d'organisme (#125 - par @jpm-cbna) -* Correction de la modification du mot de passe d'un utilisateur qui n'a pas de prénom ou de nom (#107 - par @jpm-cbna) -* Meilleure portabilité des scripts dans les différents systèmes Unix (#176 - par @MathRdt) +- Correction de la route de redirection après login ou un logout pour éviter les erreurs de redirection lorsque l'URL de l'application n'est pas située à la racine (#173 / #174 - par @joelclems) +- Création de compte : affichage d'un message explicite lorsque le token d'un utlisateur temporaire n'est pas trouvé quand un administrateur cherche à le valider (#177 / https://github.com/PnX-SI/GeoNature/issues/862 - par @joelclems) +- Correction de l'affichage de la fiche d'un utilisateur si celui-ci n'a pas d'organisme (#125 - par @jpm-cbna) +- Correction de la modification du mot de passe d'un utilisateur qui n'a pas de prénom ou de nom (#107 - par @jpm-cbna) +- Meilleure portabilité des scripts dans les différents systèmes Unix (#176 - par @MathRdt) 2.3.3 (2023-02-16) ------------------ **🚀 Nouveautés** -* Docker : l’image est construite avec la version des modules référencés par les sous-modules Git +- Docker : l’image est construite avec la version des modules référencés par les sous-modules Git **🐛 Corrections** -* Correction de l'affichage des rôles associés à une liste (#165) -* Correction de la route permettant la modification du mot de passe (https://github.com/PnX-SI/GeoNature/issues/2288) -* Mise à jour de ``UsersHub-authentification-module`` en version corrective ``1.6.2`` +- Correction de l'affichage des rôles associés à une liste (#165) +- Correction de la route permettant la modification du mot de passe (https://github.com/PnX-SI/GeoNature/issues/2288) +- Mise à jour de ``UsersHub-authentification-module`` en version corrective ``1.6.2`` 2.3.2 (2022-11-23) @@ -40,17 +43,17 @@ CHANGELOG **🚀 Nouveautés** -* Ajout d’un ``Dockerfile`` et publication automatique des images de celui-ci par Github Action -* Ajout d’un fichier Docker Compose permettant de lancer UsersHub et PostgreSQL -* Support de la variable d’environnement ``USERSHUB_SETTINGS`` pour définir le fichier de configuration -* Ajout du paramètre ``CODE_APPLICATION`` (valeur par défaut : ``UH``) -* Le dossier des fichiers statiques peut être défini avec la variable d’environnement ``USERSHUB_STATIC_FOLDER`` +- Ajout d’un ``Dockerfile`` et publication automatique des images de celui-ci par Github Action +- Ajout d’un fichier Docker Compose permettant de lancer UsersHub et PostgreSQL +- Support de la variable d’environnement ``USERSHUB_SETTINGS`` pour définir le fichier de configuration +- Ajout du paramètre ``CODE_APPLICATION`` (valeur par défaut : ``UH``) +- Le dossier des fichiers statiques peut être défini avec la variable d’environnement ``USERSHUB_STATIC_FOLDER`` **🐛 Corrections** -* Correction du packaging : incorporation des templates, des fichiers Alembic -* Suppression de la dépendance à PostgreSQL dans le fichier service systemd -* Déclaration des migrations Alembic dans les entry points +- Correction du packaging : incorporation des templates, des fichiers Alembic +- Suppression de la dépendance à PostgreSQL dans le fichier service systemd +- Déclaration des migrations Alembic dans les entry points 2.3.1 (2022-09-20) @@ -58,8 +61,8 @@ CHANGELOG **🐛 Corrections** -* Ajout de ``gunicorn`` aux requirements -* Ajout de ``extend_existing=True`` sur le modèle ``CorRoleListe`` +- Ajout de ``gunicorn`` aux requirements +- Ajout de ``extend_existing=True`` sur le modèle ``CorRoleListe`` 2.3.0 (2022-09-16) @@ -67,21 +70,21 @@ CHANGELOG **🚀 Nouveautés** -* Support de *Flask 2* +- Support de *Flask 2* - * Mise à jour de ``UsersHub-authentification-module`` en version ``1.6.0`` + - Mise à jour de ``UsersHub-authentification-module`` en version ``1.6.0`` -* *systemd* : Ajout d’une dépendance au service ``postgresql`` -* Amélioration de l’affichage des tables -* Fichiers de log : +- *systemd* : Ajout d’une dépendance au service ``postgresql`` +- Amélioration de l’affichage des tables +- Fichiers de log : - * Les logs sont à présent écrits dans le fichier ``/var/log/usershub/usershub.log`` - * L’outil ``logrotate`` est configuré pour assurer la rotation du fichier - * L’ancien fichier de log ``/var/log/usershub.log`` est intouché; vous pouvez le supprimer, ou l’archiver manuellement. + - Les logs sont à présent écrits dans le fichier ``/var/log/usershub/usershub.log`` + - L’outil ``logrotate`` est configuré pour assurer la rotation du fichier + - L’ancien fichier de log ``/var/log/usershub.log`` est intouché; vous pouvez le supprimer, ou l’archiver manuellement. **🐛 Corrections** -* Correction d’un import manquant +- Correction d’un import manquant 2.2.2 (2021-12-22) From 58cd2dedb03e49bbd55b6c30cd2c855ea20c97df Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Tue, 30 Jan 2024 13:52:38 +0100 Subject: [PATCH 12/15] update dependencies + requirement dev --- dependencies/UsersHub-authentification-module | 2 +- requirements-dev.txt | 56 ++++++++++--------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index 29dcb8b..279ef86 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit 29dcb8b2e9099c2c21124c8d3666f1fe69ca312d +Subproject commit 279ef8601ed1b6184e588ab8f191497b3bdddfed diff --git a/requirements-dev.txt b/requirements-dev.txt index cb88a2f..3b177e5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,36 +1,36 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.9 # by the following command: # # pip-compile requirements-dev.in # -e file:dependencies/UsersHub-authentification-module#egg=pypnusershub # via -r requirements-submodules.in -alembic==1.12.1 +alembic==1.13.1 # via # flask-migrate # pypnusershub -authlib==1.2.1 +authlib==1.3.0 # via pypnusershub -bcrypt==4.0.1 +bcrypt==4.1.2 # via pypnusershub -blinker==1.6.3 +blinker==1.7.0 # via flask -certifi==2023.7.22 +certifi==2023.11.17 # via requests cffi==1.16.0 # via cryptography -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 # via requests click==8.1.7 # via flask -cryptography==41.0.5 +cryptography==42.0.1 # via authlib -dnspython==2.4.2 +dnspython==2.5.0 # via email-validator email-validator==2.1.0.post1 # via wtforms-components -flask==2.3.3 +flask==3.0.1 # via # -r requirements-common.in # flask-login @@ -42,7 +42,7 @@ flask==2.3.3 # utils-flask-sqlalchemy flask-login==0.6.3 # via pypnusershub -flask-marshmallow==0.15.0 +flask-marshmallow==1.1.0 # via pypnusershub flask-migrate==4.0.5 # via @@ -56,14 +56,16 @@ flask-sqlalchemy==3.0.5 # utils-flask-sqlalchemy flask-wtf==1.2.1 # via -r requirements-common.in -greenlet==3.0.1 +greenlet==3.0.3 # via sqlalchemy gunicorn==21.2.0 # via -r requirements-common.in -idna==3.4 +idna==3.6 # via # email-validator # requests +importlib-metadata==7.0.1 + # via flask infinity==1.5 # via intervals intervals==0.9.2 @@ -72,29 +74,28 @@ itsdangerous==2.1.2 # via # flask # flask-wtf -jinja2==3.1.2 +jinja2==3.1.3 # via flask -mako==1.2.4 +mako==1.3.0 # via alembic -markupsafe==2.1.3 +markupsafe==2.1.4 # via # jinja2 # mako # werkzeug # wtforms # wtforms-components -marshmallow==3.20.1 +marshmallow==3.20.2 # via # flask-marshmallow # marshmallow-sqlalchemy # utils-flask-sqlalchemy -marshmallow-sqlalchemy==0.29.0 +marshmallow-sqlalchemy==0.30.0 # via # -r requirements-common.in # pypnusershub packaging==23.2 # via - # flask-marshmallow # gunicorn # marshmallow # marshmallow-sqlalchemy @@ -108,7 +109,7 @@ python-dateutil==2.8.2 # via # -r requirements-common.in # utils-flask-sqlalchemy -python-dotenv==1.0.0 +python-dotenv==1.0.1 # via -r requirements-common.in requests==2.31.0 # via pypnusershub @@ -116,32 +117,33 @@ six==1.16.0 # via # python-dateutil # wtforms-components -sqlalchemy==1.4.50 +sqlalchemy==1.4.51 # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # pypnusershub # utils-flask-sqlalchemy -typing-extensions==4.8.0 +typing-extensions==4.9.0 # via # alembic # sqlalchemy -urllib3==2.0.7 +urllib3==2.1.0 # via requests -utils-flask-sqlalchemy==0.3.6 +utils-flask-sqlalchemy==0.4.1 # via pypnusershub validators==0.22.0 # via wtforms-components -werkzeug==2.3.7 +werkzeug==3.0.1 # via # flask # flask-login - # pypnusershub -wtforms==3.1.0 +wtforms==3.1.2 # via # -r requirements-common.in # flask-wtf # wtforms-components wtforms-components==0.10.5 # via -r requirements-common.in +zipp==3.17.0 + # via importlib-metadata From 7de21ce7f3c709439ccbb4cc0eb8a3b926646c1e Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Tue, 30 Jan 2024 14:54:28 +0100 Subject: [PATCH 13/15] update userhub-auth-module --- dependencies/UsersHub-authentification-module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index 279ef86..40ec9e0 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit 279ef8601ed1b6184e588ab8f191497b3bdddfed +Subproject commit 40ec9e0a9c0cb0dd5e788e48cb93c7d1963737c0 From 4f335b7c3210165ac1e4876213c5ed76078e971f Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Tue, 30 Jan 2024 15:19:35 +0100 Subject: [PATCH 14/15] update requirements + update UH-authentification-module dependency --- dependencies/UsersHub-authentification-module | 2 +- requirements-dependencies.in | 2 +- requirements-dev.txt | 2 +- requirements.txt | 60 +++++++++---------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module index 40ec9e0..5e85d6c 160000 --- a/dependencies/UsersHub-authentification-module +++ b/dependencies/UsersHub-authentification-module @@ -1 +1 @@ -Subproject commit 40ec9e0a9c0cb0dd5e788e48cb93c7d1963737c0 +Subproject commit 5e85d6c736232728a3d6c03c22bfbde34daeb40f diff --git a/requirements-dependencies.in b/requirements-dependencies.in index d57fdf5..a7f78ff 100644 --- a/requirements-dependencies.in +++ b/requirements-dependencies.in @@ -1 +1 @@ -pypnusershub>=2.0.0,<3.0.0 \ No newline at end of file +pypnusershub>=2.1.1,<3.0.0 \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 3b177e5..9c3291f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -76,7 +76,7 @@ itsdangerous==2.1.2 # flask-wtf jinja2==3.1.3 # via flask -mako==1.3.0 +mako==1.3.2 # via alembic markupsafe==2.1.4 # via diff --git a/requirements.txt b/requirements.txt index 5af04cb..c904c09 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,34 +1,34 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.9 # by the following command: # # pip-compile requirements.in # -alembic==1.12.1 +alembic==1.13.1 # via # flask-migrate # pypnusershub -authlib==1.2.1 +authlib==1.3.0 # via pypnusershub -bcrypt==4.0.1 +bcrypt==4.1.2 # via pypnusershub -blinker==1.6.3 +blinker==1.7.0 # via flask -certifi==2023.7.22 +certifi==2023.11.17 # via requests cffi==1.16.0 # via cryptography -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 # via requests click==8.1.7 # via flask -cryptography==41.0.5 +cryptography==42.0.1 # via authlib -dnspython==2.4.2 +dnspython==2.5.0 # via email-validator email-validator==2.1.0.post1 # via wtforms-components -flask==2.3.3 +flask==3.0.1 # via # -r requirements-common.in # flask-login @@ -40,7 +40,7 @@ flask==2.3.3 # utils-flask-sqlalchemy flask-login==0.6.3 # via pypnusershub -flask-marshmallow==0.15.0 +flask-marshmallow==1.1.0 # via pypnusershub flask-migrate==4.0.5 # via @@ -54,14 +54,16 @@ flask-sqlalchemy==3.0.5 # utils-flask-sqlalchemy flask-wtf==1.2.1 # via -r requirements-common.in -greenlet==3.0.1 +greenlet==3.0.3 # via sqlalchemy gunicorn==21.2.0 # via -r requirements-common.in -idna==3.4 +idna==3.6 # via # email-validator # requests +importlib-metadata==7.0.1 + # via flask infinity==1.5 # via intervals intervals==0.9.2 @@ -70,29 +72,28 @@ itsdangerous==2.1.2 # via # flask # flask-wtf -jinja2==3.1.2 +jinja2==3.1.3 # via flask -mako==1.2.4 +mako==1.3.2 # via alembic -markupsafe==2.1.3 +markupsafe==2.1.4 # via # jinja2 # mako # werkzeug # wtforms # wtforms-components -marshmallow==3.20.1 +marshmallow==3.20.2 # via # flask-marshmallow # marshmallow-sqlalchemy # utils-flask-sqlalchemy -marshmallow-sqlalchemy==0.29.0 +marshmallow-sqlalchemy==0.30.0 # via # -r requirements-common.in # pypnusershub packaging==23.2 # via - # flask-marshmallow # gunicorn # marshmallow # marshmallow-sqlalchemy @@ -102,15 +103,13 @@ psycopg2==2.9.9 # pypnusershub pycparser==2.21 # via cffi -pyparsing==3.0.9 - # via packaging -pypnusershub==2.0.0 +pypnusershub==2.1.1 # via -r requirements-dependencies.in python-dateutil==2.8.2 # via # -r requirements-common.in # utils-flask-sqlalchemy -python-dotenv==1.0.0 +python-dotenv==1.0.1 # via -r requirements-common.in requests==2.31.0 # via pypnusershub @@ -118,32 +117,33 @@ six==1.16.0 # via # python-dateutil # wtforms-components -sqlalchemy==1.4.50 +sqlalchemy==1.4.51 # via # alembic # flask-sqlalchemy # marshmallow-sqlalchemy # pypnusershub # utils-flask-sqlalchemy -typing-extensions==4.8.0 +typing-extensions==4.9.0 # via # alembic # sqlalchemy -urllib3==2.0.7 +urllib3==2.1.0 # via requests -utils-flask-sqlalchemy==0.3.6 +utils-flask-sqlalchemy==0.4.1 # via pypnusershub validators==0.22.0 # via wtforms-components -werkzeug==2.3.7 +werkzeug==3.0.1 # via # flask # flask-login - # pypnusershub -wtforms==3.1.0 +wtforms==3.1.2 # via # -r requirements-common.in # flask-wtf # wtforms-components wtforms-components==0.10.5 # via -r requirements-common.in +zipp==3.17.0 + # via importlib-metadata From f4f270f73c03c69bcd4c8bf1c7c0027f6f4662cb Mon Sep 17 00:00:00 2001 From: Jacobe2169 Date: Tue, 30 Jan 2024 15:23:29 +0100 Subject: [PATCH 15/15] update changelog --- docs/changelog.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c3ac140..09cb407 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,14 +2,16 @@ CHANGELOG ========= -2.4.0 (unreleased) +2.4.0 (2024-01-30) ------------------ **🚀 Nouveautés** -- Passage à Flask-Login pour la gestion de l'authentification via la mise à jour de UsersHub-authentification-module en version 2.0.0 (#188) +- Passage à Flask-Login pour la gestion de l'authentification via la mise à jour de UsersHub-authentification-module en version 2.1.1 (#188) +- Passage de Flask en version 3.0 +- Passage de SQLAlchemy en 1.4 - Mise à jour de nombreuses dépendances Python -- Mise à jour du linter black en version 24 +- Mise à jour du linter black en version 24 (#193) - Rechargement automatique du Docker quand on modifie le fichier de configuration de UsersHub