From c8801f6ee0b971e51cf47c612b9ae2adb06a4bd4 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 22 Feb 2023 02:13:06 +0100 Subject: [PATCH] API: unification codes erreur HTTP + check group/partition names --- app/api/__init__.py | 3 +++ app/api/absences.py | 2 +- app/api/billets_absences.py | 3 +-- app/api/departements.py | 8 +++---- app/api/etudiants.py | 2 +- app/api/evaluations.py | 1 - app/api/formsemestres.py | 8 +++---- app/api/jury.py | 4 +--- app/api/partitions.py | 46 ++++++++++++++++++++++--------------- app/api/users.py | 6 ++--- app/models/groups.py | 4 ++-- sco_version.py | 2 +- 12 files changed, 49 insertions(+), 40 deletions(-) diff --git a/app/api/__init__.py b/app/api/__init__.py index 886c26f8..90744703 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -9,6 +9,9 @@ from app.scodoc.sco_exceptions import ScoException api_bp = Blueprint("api", __name__) api_web_bp = Blueprint("apiweb", __name__) +# HTTP ERROR STATUS +API_CLIENT_ERROR = 400 # erreur dans les paramètres fournis par le client + @api_bp.errorhandler(ScoException) @api_bp.errorhandler(404) diff --git a/app/api/absences.py b/app/api/absences.py index b049bfb7..808fce3c 100644 --- a/app/api/absences.py +++ b/app/api/absences.py @@ -8,7 +8,7 @@ from flask import jsonify -from app.api import api_bp as bp +from app.api import api_bp as bp, API_CLIENT_ERROR from app.scodoc.sco_utils import json_error from app.decorators import scodoc, permission_required from app.models import Identite diff --git a/app/api/billets_absences.py b/app/api/billets_absences.py index 69d6186f..7ed0f119 100644 --- a/app/api/billets_absences.py +++ b/app/api/billets_absences.py @@ -11,7 +11,6 @@ from flask import g, jsonify, request from flask_login import login_required -import app from app import db from app.api import api_bp as bp, api_web_bp from app.decorators import scodoc, permission_required @@ -48,7 +47,7 @@ def billets_absence_create(): justified = data.get("justified", False) if None in (etudid, abs_begin, abs_end): return json_error( - 404, message="Paramètre manquant: etudid, abs_bein, abs_end requis" + 404, message="Paramètre manquant: etudid, abs_begin, abs_end requis" ) query = Identite.query.filter_by(etudid=etudid) if g.scodoc_dept: diff --git a/app/api/departements.py b/app/api/departements.py index bc6c38d6..d7ce28ef 100644 --- a/app/api/departements.py +++ b/app/api/departements.py @@ -17,7 +17,7 @@ from flask_login import login_required import app from app import db -from app.api import api_bp as bp +from app.api import api_bp as bp, API_CLIENT_ERROR from app.scodoc.sco_utils import json_error from app.decorators import scodoc, permission_required from app.models import Departement, FormSemestre @@ -105,12 +105,12 @@ def departement_create(): data = request.get_json(force=True) # may raise 400 Bad Request acronym = str(data.get("acronym", "")) if not acronym: - return json_error(404, "missing acronym") + return json_error(API_CLIENT_ERROR, "missing acronym") visible = bool(data.get("visible", True)) try: dept = departements.create_dept(acronym, visible=visible) except ScoValueError as exc: - return json_error(404, exc.args[0] if exc.args else "") + return json_error(500, exc.args[0] if exc.args else "") return jsonify(dept.to_dict()) @@ -130,7 +130,7 @@ def departement_edit(acronym): data = request.get_json(force=True) # may raise 400 Bad Request visible = bool(data.get("visible", None)) if visible is None: - return json_error(404, "missing argument: visible") + return json_error(API_CLIENT_ERROR, "missing argument: visible") visible = bool(visible) dept.visible = visible db.session.add(dept) diff --git a/app/api/etudiants.py b/app/api/etudiants.py index 4ee02a29..c6d8cf2b 100644 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -265,7 +265,7 @@ def bulletin( formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404() dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404() if g.scodoc_dept and dept.acronym != g.scodoc_dept: - return json_error(404, "formsemestre non trouve") + return json_error(404, "formsemestre inexistant") app.set_sco_dept(dept.acronym) if code_type == "nip": diff --git a/app/api/evaluations.py b/app/api/evaluations.py index 69e18e7b..5dd0f057 100644 --- a/app/api/evaluations.py +++ b/app/api/evaluations.py @@ -15,7 +15,6 @@ import app from app.api import api_bp as bp, api_web_bp from app.decorators import scodoc, permission_required -from app.scodoc.sco_utils import json_error from app.models import Evaluation, ModuleImpl, FormSemestre from app.scodoc import sco_evaluation_db from app.scodoc.sco_permissions import Permission diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index 9078cebb..b7673ffe 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -11,7 +11,7 @@ from flask import g, jsonify, request from flask_login import login_required import app -from app.api import api_bp as bp, api_web_bp +from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR from app.decorators import scodoc, permission_required from app.scodoc.sco_utils import json_error from app.comp import res_sem @@ -113,7 +113,7 @@ def formsemestres_query(): try: annee_scolaire_int = int(annee_scolaire) except ValueError: - return json_error(404, "invalid annee_scolaire: not int") + return json_error(API_CLIENT_ERROR, "invalid annee_scolaire: not int") debut_annee = scu.date_debut_anne_scolaire(annee_scolaire_int) fin_annee = scu.date_fin_anne_scolaire(annee_scolaire_int) formsemestres = formsemestres.filter( @@ -125,7 +125,7 @@ def formsemestres_query(): try: dept_id = int(dept_id) except ValueError: - return json_error(404, "invalid dept_id: not int") + return json_error(404, "invalid dept_id: integer expected") formsemestres = formsemestres.filter_by(dept_id=dept_id) if etape_apo is not None: formsemestres = formsemestres.join(FormSemestreEtape).filter( @@ -468,7 +468,7 @@ def formsemestre_resultat(formsemestre_id: int): """ format_spec = request.args.get("format", None) if format_spec is not None and format_spec != "raw": - return json_error(404, "invalid format specification") + return json_error(API_CLIENT_ERROR, "invalid format specification") convert_values = format_spec != "raw" query = FormSemestre.query.filter_by(id=formsemestre_id) diff --git a/app/api/jury.py b/app/api/jury.py index 383396e5..24ff2c27 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -8,15 +8,13 @@ ScoDoc 9 API : jury WIP """ -from flask import g, jsonify, request +from flask import jsonify from flask_login import login_required import app -from app import db, log from app.api import api_bp as bp, api_web_bp from app.decorators import scodoc, permission_required from app.scodoc.sco_exceptions import ScoException -from app.scodoc.sco_utils import json_error from app.but import jury_but_results from app.models import FormSemestre from app.scodoc.sco_permissions import Permission diff --git a/app/api/partitions.py b/app/api/partitions.py index 489d20e8..bfd1bca6 100644 --- a/app/api/partitions.py +++ b/app/api/partitions.py @@ -12,7 +12,7 @@ from flask_login import login_required import app from app import db, log -from app.api import api_bp as bp, api_web_bp +from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR from app.decorators import scodoc, permission_required from app.scodoc.sco_utils import json_error from app.models import FormSemestre, FormSemestreInscription, Identite @@ -138,7 +138,7 @@ def etud_in_group_query(group_id: int): """Étudiants du groupe, filtrés par état""" etat = request.args.get("etat") if etat not in {None, scu.INSCRIT, scu.DEMISSION, scu.DEF}: - return json_error(404, "etat: valeur invalide") + return json_error(API_CLIENT_ERROR, "etat: valeur invalide") query = GroupDescr.query.filter_by(id=group_id) if g.scodoc_dept: query = ( @@ -281,9 +281,9 @@ def group_create(partition_id: int): data = request.get_json(force=True) # may raise 400 Bad Request group_name = data.get("group_name") if group_name is None: - return json_error(404, "missing group name or invalid data format") + return json_error(API_CLIENT_ERROR, "missing group name or invalid data format") if not GroupDescr.check_name(partition, group_name): - return json_error(404, "invalid group_name") + return json_error(API_CLIENT_ERROR, "invalid group_name") group_name = group_name.strip() group = GroupDescr(group_name=group_name, partition_id=partition_id) @@ -343,7 +343,7 @@ def group_edit(group_id: int): if group_name is not None: group_name = group_name.strip() if not GroupDescr.check_name(group.partition, group_name, existing=True): - return json_error(404, "invalid group_name") + return json_error(API_CLIENT_ERROR, "invalid group_name") group.group_name = group_name db.session.add(group) db.session.commit() @@ -381,14 +381,18 @@ def partition_create(formsemestre_id: int): data = request.get_json(force=True) # may raise 400 Bad Request partition_name = data.get("partition_name") if partition_name is None: - return json_error(404, "missing partition_name or invalid data format") + return json_error( + API_CLIENT_ERROR, "missing partition_name or invalid data format" + ) if partition_name == scu.PARTITION_PARCOURS: - return json_error(404, f"invalid partition_name {scu.PARTITION_PARCOURS}") + return json_error( + API_CLIENT_ERROR, f"invalid partition_name {scu.PARTITION_PARCOURS}" + ) if not Partition.check_name(formsemestre, partition_name): - return json_error(404, "invalid partition_name") + return json_error(API_CLIENT_ERROR, "invalid partition_name") numero = data.get("numero", 0) if not isinstance(numero, int): - return json_error(404, "invalid type for numero") + return json_error(API_CLIENT_ERROR, "invalid type for numero") args = { "formsemestre_id": formsemestre_id, "partition_name": partition_name.strip(), @@ -399,7 +403,7 @@ def partition_create(formsemestre_id: int): boolean_field, False if boolean_field != "groups_editable" else True ) if not isinstance(value, bool): - return json_error(404, f"invalid type for {boolean_field}") + return json_error(API_CLIENT_ERROR, f"invalid type for {boolean_field}") args[boolean_field] = value partition = Partition(**args) @@ -433,7 +437,7 @@ def formsemestre_order_partitions(formsemestre_id: int): isinstance(x, int) for x in partition_ids ): return json_error( - 404, + API_CLIENT_ERROR, message="paramètre liste des partitions invalide", ) for p_id, numero in zip(partition_ids, range(len(partition_ids))): @@ -472,7 +476,7 @@ def partition_order_groups(partition_id: int): isinstance(x, int) for x in group_ids ): return json_error( - 404, + API_CLIENT_ERROR, message="paramètre liste de groupe invalide", ) for group_id, numero in zip(group_ids, range(len(group_ids))): @@ -516,18 +520,20 @@ def partition_edit(partition_id: int): # if partition_name is not None and partition_name != partition.partition_name: if partition.is_parcours(): - return json_error(404, f"can't rename {scu.PARTITION_PARCOURS}") + return json_error( + API_CLIENT_ERROR, f"can't rename {scu.PARTITION_PARCOURS}" + ) if not Partition.check_name( partition.formsemestre, partition_name, existing=True ): - return json_error(404, "invalid partition_name") + return json_error(API_CLIENT_ERROR, "invalid partition_name") partition.partition_name = partition_name.strip() modified = True numero = data.get("numero") if numero is not None and numero != partition.numero: if not isinstance(numero, int): - return json_error(404, "invalid type for numero") + return json_error(API_CLIENT_ERROR, "invalid type for numero") partition.numero = numero modified = True @@ -535,9 +541,11 @@ def partition_edit(partition_id: int): value = data.get(boolean_field) if value is not None and value != getattr(partition, boolean_field): if not isinstance(value, bool): - return json_error(404, f"invalid type for {boolean_field}") + return json_error(API_CLIENT_ERROR, f"invalid type for {boolean_field}") if boolean_field == "groups_editable" and partition.is_parcours(): - return json_error(404, f"can't change {scu.PARTITION_PARCOURS}") + return json_error( + API_CLIENT_ERROR, f"can't change {scu.PARTITION_PARCOURS}" + ) setattr(partition, boolean_field, value) modified = True @@ -571,7 +579,9 @@ def partition_delete(partition_id: int): if not partition.formsemestre.etat: return json_error(403, "formsemestre verrouillé") if not partition.partition_name: - return json_error(404, "ne peut pas supprimer la partition par défaut") + return json_error( + API_CLIENT_ERROR, "ne peut pas supprimer la partition par défaut" + ) is_parcours = partition.is_parcours() formsemestre: FormSemestre = partition.formsemestre log(f"deleting partition {partition}") diff --git a/app/api/users.py b/app/api/users.py index 962aa9e7..ab00f0a7 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -12,8 +12,8 @@ from flask import g, jsonify, request from flask_login import current_user, login_required -from app import db, log -from app.api import api_bp as bp, api_web_bp +from app import db +from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR from app.scodoc.sco_utils import json_error from app.auth.models import User, Role, UserRole from app.auth.models import is_valid_password @@ -187,7 +187,7 @@ def user_password(uid: int): if not password: return json_error(404, "user_password: missing password") if not is_valid_password(password): - return json_error(400, "user_password: invalid password") + return json_error(API_CLIENT_ERROR, "user_password: invalid password") allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin) if (None not in allowed_depts) and ((user.dept not in allowed_depts)): return json_error(403, "user_password: departement non autorise") diff --git a/app/models/groups.py b/app/models/groups.py index 6fafa234..b30d1b4d 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -72,7 +72,7 @@ class Partition(db.Model): """ if not isinstance(partition_name, str): return False - if not len(partition_name.strip()) > 0: + if not (0 < len(partition_name.strip()) < SHORT_STR_LEN): return False if (not existing) and ( partition_name in [p.partition_name for p in formsemestre.partitions] @@ -147,7 +147,7 @@ class GroupDescr(db.Model): """ if not isinstance(group_name, str): return False - if not default and not len(group_name.strip()) > 0: + if not default and not (0 < len(group_name.strip()) < GROUPNAME_STR_LEN): return False if (not existing) and (group_name in [g.group_name for g in partition.groups]): return False diff --git a/sco_version.py b/sco_version.py index 6fe40c05..b3722bb1 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.4.47" +SCOVERSION = "9.4.48" SCONAME = "ScoDoc"