API: modification pour accès via cookie web

This commit is contained in:
Emmanuel Viennet 2022-07-22 16:39:21 +02:00
parent cfd4448ca5
commit aa1ec6fd8e
12 changed files with 114 additions and 127 deletions

View File

@ -10,20 +10,18 @@ from flask import jsonify
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import Identite from app.models import Identite
from app.scodoc import notesdb as ndb from app.scodoc import notesdb as ndb
from app.scodoc import sco_abs from app.scodoc import sco_abs
# from app.scodoc.sco_abs import annule_absence, annule_justif, add_abslist
from app.scodoc.sco_groups import get_group_members from app.scodoc.sco_groups import get_group_members
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"]) @bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def absences(etudid: int = None): def absences(etudid: int = None):
""" """
Retourne la liste des absences d'un étudiant donné Retourne la liste des absences d'un étudiant donné
@ -67,8 +65,7 @@ def absences(etudid: int = None):
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"]) @bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def absences_just(etudid: int = None): def absences_just(etudid: int = None):
""" """
Retourne la liste des absences justifiées d'un étudiant donné Retourne la liste des absences justifiées d'un étudiant donné
@ -123,8 +120,7 @@ def absences_just(etudid: int = None):
"/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>", "/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None): def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
""" """
Liste des absences d'un groupe (possibilité de choisir entre deux dates) Liste des absences d'un groupe (possibilité de choisir entre deux dates)
@ -170,11 +166,11 @@ def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
data = [] data = []
# Filtre entre les deux dates renseignées # Filtre entre les deux dates renseignées
for member in members: for member in members:
abs = { absence = {
"etudid": member["etudid"], "etudid": member["etudid"],
"list_abs": sco_abs.list_abs_date(member["etudid"], date_debut, date_fin), "list_abs": sco_abs.list_abs_date(member["etudid"], date_debut, date_fin),
} }
data.append(abs) data.append(absence)
return jsonify(data) return jsonify(data)

View File

@ -26,10 +26,9 @@
from functools import wraps from functools import wraps
from flask import abort
from flask import g from flask import g
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
from flask_login import current_user
from app import log from app import log
from app.auth.models import User from app.auth.models import User
@ -57,7 +56,10 @@ def basic_auth_error(status):
@token_auth.verify_token @token_auth.verify_token
def verify_token(token) -> User: def verify_token(token) -> User:
"Retrouve l'utilisateur à partir du jeton" """Retrouve l'utilisateur à partir du jeton.
Si la requête n'a pas de jeton, token == "".
"""
user = User.check_token(token) if token else None user = User.check_token(token) if token else None
g.current_user = user g.current_user = user
return user return user
@ -65,7 +67,7 @@ def verify_token(token) -> User:
@token_auth.error_handler @token_auth.error_handler
def token_auth_error(status): def token_auth_error(status):
"rréponse en cas d'erreur d'auth." "Réponse en cas d'erreur d'auth."
return error_response(status) return error_response(status)
@ -75,7 +77,7 @@ def get_user_roles(user):
def token_permission_required(permission): def token_permission_required(permission):
"Décorateur pour les fontions de l'API ScoDoc" "Décorateur pour les fonctions de l'API ScoDoc"
def decorator(f): def decorator(f):
@wraps(f) @wraps(f)
@ -84,13 +86,39 @@ def token_permission_required(permission):
current_user = basic_auth.current_user() current_user = basic_auth.current_user()
if not current_user or not current_user.has_permission(permission, None): if not current_user or not current_user.has_permission(permission, None):
if current_user: if current_user:
log(f"API permission denied (user {current_user})") message = f"API permission denied (user {current_user})"
else: else:
log("API permission denied (no user supplied)") message = f"API permission denied (no user supplied)"
abort(403) log(message)
# raise werkzeug.exceptions.Forbidden(description=message)
return error_response(403, message=None)
return f(*args, **kwargs) return f(*args, **kwargs)
# return decorated_function(token_auth.login_required()) # return decorated_function(token_auth.login_required())
return decorated_function return decorated_function
return decorator return decorator
def permission_required_api(permission_web, permission_api):
"""Décorateur pour les fonctions de l'API accessibles en mode jeton
ou en mode web.
Si cookie d'authentification web, utilise pour se logger et calculer les
permissions.
Sinon, tente le jeton jwt.
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
scodoc_dept = getattr(g, "scodoc_dept", None)
if not current_user.has_permission(permission_web, scodoc_dept):
# try API
return token_auth.login_required(
token_permission_required(permission_api)(f)
)(*args, **kwargs)
return f(*args, **kwargs)
return decorated_function
return decorator

View File

@ -5,7 +5,7 @@ from flask import jsonify
import app import app
from app import models from app import models
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import Departement, FormSemestre from app.models import Departement, FormSemestre
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -22,24 +22,21 @@ def get_departement(dept_ident: str) -> Departement:
@bp.route("/departements", methods=["GET"]) @bp.route("/departements", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def departements(): def departements():
"""Liste les départements""" """Liste les départements"""
return jsonify([dept.to_dict() for dept in Departement.query]) return jsonify([dept.to_dict() for dept in Departement.query])
@bp.route("/departements_ids", methods=["GET"]) @bp.route("/departements_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def departements_ids(): def departements_ids():
"""Liste des ids de départements""" """Liste des ids de départements"""
return jsonify([dept.id for dept in Departement.query]) return jsonify([dept.id for dept in Departement.query])
@bp.route("/departement/<string:acronym>", methods=["GET"]) @bp.route("/departement/<string:acronym>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def departement(acronym: str): def departement(acronym: str):
""" """
Info sur un département. Accès par acronyme. Info sur un département. Accès par acronyme.
@ -58,8 +55,7 @@ def departement(acronym: str):
@bp.route("/departement/id/<int:dept_id>", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def departement_by_id(dept_id: int): def departement_by_id(dept_id: int):
""" """
Info sur un département. Accès par id. Info sur un département. Accès par id.
@ -69,8 +65,7 @@ def departement_by_id(dept_id: int):
@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"]) @bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_etudiants(acronym: str): def dept_etudiants(acronym: str):
""" """
Retourne la liste des étudiants d'un département Retourne la liste des étudiants d'un département
@ -98,8 +93,7 @@ def dept_etudiants(acronym: str):
@bp.route("/departement/id/<int:dept_id>/etudiants", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>/etudiants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_etudiants_by_id(dept_id: int): def dept_etudiants_by_id(dept_id: int):
""" """
Retourne la liste des étudiants d'un département d'id donné. Retourne la liste des étudiants d'un département d'id donné.
@ -109,8 +103,7 @@ def dept_etudiants_by_id(dept_id: int):
@bp.route("/departement/<string:acronym>/formsemestres_ids", methods=["GET"]) @bp.route("/departement/<string:acronym>/formsemestres_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_formsemestres_ids(acronym: str): def dept_formsemestres_ids(acronym: str):
"""liste des ids formsemestre du département""" """liste des ids formsemestre du département"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404() dept = Departement.query.filter_by(acronym=acronym).first_or_404()
@ -118,8 +111,7 @@ def dept_formsemestres_ids(acronym: str):
@bp.route("/departement/id/<int:dept_id>/formsemestres_ids", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>/formsemestres_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_formsemestres_ids_by_id(dept_id: int): def dept_formsemestres_ids_by_id(dept_id: int):
"""liste des ids formsemestre du département""" """liste des ids formsemestre du département"""
dept = Departement.query.get_or_404(dept_id) dept = Departement.query.get_or_404(dept_id)
@ -127,8 +119,7 @@ def dept_formsemestres_ids_by_id(dept_id: int):
@bp.route("/departement/<string:acronym>/formsemestres_courants", methods=["GET"]) @bp.route("/departement/<string:acronym>/formsemestres_courants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_formsemestres_courants(acronym: str): def dept_formsemestres_courants(acronym: str):
""" """
Liste des semestres actifs d'un département d'acronyme donné Liste des semestres actifs d'un département d'acronyme donné
@ -182,8 +173,7 @@ def dept_formsemestres_courants(acronym: str):
@bp.route("/departement/id/<int:dept_id>/formsemestres_courants", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>/formsemestres_courants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def dept_formsemestres_courants_by_id(dept_id: int): def dept_formsemestres_courants_by_id(dept_id: int):
""" """
Liste des semestres actifs d'un département d'id donné Liste des semestres actifs d'un département d'id donné

View File

@ -27,6 +27,7 @@ from werkzeug.http import HTTP_STATUS_CODES
def error_response(status_code, message=None): def error_response(status_code, message=None):
"""Réponse sur erreur"""
payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")} payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")}
if message: if message:
payload["message"] = message payload["message"] = message
@ -36,4 +37,5 @@ def error_response(status_code, message=None):
def bad_request(message): def bad_request(message):
"400 Bad Request response"
return error_response(400, message) return error_response(400, message)

View File

@ -8,25 +8,23 @@
API : accès aux étudiants API : accès aux étudiants
""" """
from flask import jsonify, make_response from flask import jsonify
import app import app
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.api import tools from app.api import tools
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@bp.route("/etudiants/courants", defaults={"long": False}) @bp.route("/etudiants/courants", defaults={"long": False})
@bp.route("/etudiants/courants/long", defaults={"long": True}) @bp.route("/etudiants/courants/long", defaults={"long": True})
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiants_courants(long=False): def etudiants_courants(long=False):
""" """
Liste des étudiants inscrits dans un formsemestre actuellement en cours. Liste des étudiants inscrits dans un formsemestre actuellement en cours.
@ -66,8 +64,7 @@ def etudiants_courants(long=False):
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"]) @bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<string:nip>", methods=["GET"]) @bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"]) @bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiant(etudid: int = None, nip: str = None, ine: str = None): def etudiant(etudid: int = None, nip: str = None, ine: str = None):
""" """
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé. Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
@ -121,8 +118,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"]) @bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"]) @bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"]) @bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiants(etudid: int = None, nip: str = None, ine: str = None): def etudiants(etudid: int = None, nip: str = None, ine: str = None):
""" """
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
@ -149,8 +145,7 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres") @bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<string:nip>/formsemestres") @bp.route("/etudiant/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<string:ine>/formsemestres") @bp.route("/etudiant/ine/<string:ine>/formsemestres")
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None): def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
""" """
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique. Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
@ -282,8 +277,7 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
methods=["GET"], methods=["GET"],
defaults={"version": "short", "pdf": True}, defaults={"version": "short", "pdf": True},
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner en version pdf def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner en version pdf
formsemestre_id, formsemestre_id,
etudid: int = None, etudid: int = None,
@ -349,8 +343,7 @@ def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiant_groups( def etudiant_groups(
formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None
): ):

View File

@ -14,7 +14,7 @@ import app
from app import models from app import models
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.api.errors import error_response from app.api.errors import error_response
from app.models import Evaluation from app.models import Evaluation
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
@ -22,8 +22,7 @@ from app.scodoc.sco_permissions import Permission
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"]) @bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def evaluations(moduleimpl_id: int): def evaluations(moduleimpl_id: int):
""" """
Retourne la liste des évaluations d'un moduleimpl Retourne la liste des évaluations d'un moduleimpl
@ -65,8 +64,7 @@ def evaluations(moduleimpl_id: int):
@bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"]) @bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def evaluation_notes(evaluation_id: int): def evaluation_notes(evaluation_id: int):
""" """
Retourne la liste des notes à partir de l'id d'une évaluation donnée Retourne la liste des notes à partir de l'id d'une évaluation donnée

View File

@ -14,15 +14,14 @@ import app
from app import models from app import models
from app.api import bp from app.api import bp
from app.api.errors import error_response from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models.formations import Formation from app.models.formations import Formation
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/formations", methods=["GET"]) @bp.route("/formations", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formations(): def formations():
""" """
Retourne la liste de toutes les formations (tous départements) Retourne la liste de toutes les formations (tous départements)
@ -33,8 +32,7 @@ def formations():
@bp.route("/formations_ids", methods=["GET"]) @bp.route("/formations_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formations_ids(): def formations_ids():
""" """
Retourne la liste de toutes les id de formations (tous départements) Retourne la liste de toutes les id de formations (tous départements)
@ -46,8 +44,7 @@ def formations_ids():
@bp.route("/formation/<int:formation_id>", methods=["GET"]) @bp.route("/formation/<int:formation_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formation_by_id(formation_id: int): def formation_by_id(formation_id: int):
""" """
La formation d'id donné La formation d'id donné
@ -83,8 +80,7 @@ def formation_by_id(formation_id: int):
methods=["GET"], methods=["GET"],
defaults={"export_ids": True}, defaults={"export_ids": True},
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formation_export_by_formation_id(formation_id: int, export_ids=False): def formation_export_by_formation_id(formation_id: int, export_ids=False):
""" """
Retourne la formation, avec UE, matières, modules Retourne la formation, avec UE, matières, modules
@ -192,8 +188,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
@bp.route("/formation/moduleimpl/<int:moduleimpl_id>", methods=["GET"]) @bp.route("/formation/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def moduleimpl(moduleimpl_id: int): def moduleimpl(moduleimpl_id: int):
""" """
Retourne un module moduleimpl en fonction de son id Retourne un module moduleimpl en fonction de son id
@ -237,8 +232,7 @@ def moduleimpl(moduleimpl_id: int):
"/formation/<int:formation_id>/referentiel_competences", "/formation/<int:formation_id>/referentiel_competences",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def referentiel_competences(formation_id: int): def referentiel_competences(formation_id: int):
""" """
Retourne le référentiel de compétences Retourne le référentiel de compétences

View File

@ -12,7 +12,7 @@ from flask import abort, jsonify, request
import app import app
from app import models from app import models
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.comp import res_sem from app.comp import res_sem
from app.comp.moy_mod import ModuleImplResults from app.comp.moy_mod import ModuleImplResults
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
@ -25,8 +25,7 @@ import app.scodoc.sco_utils as scu
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_infos(formsemestre_id: int): def formsemestre_infos(formsemestre_id: int):
""" """
Information sur le formsemestre indiqué. Information sur le formsemestre indiqué.
@ -69,8 +68,7 @@ def formsemestre_infos(formsemestre_id: int):
@bp.route("/formsemestres/query", methods=["GET"]) @bp.route("/formsemestres/query", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestres_query(): def formsemestres_query():
""" """
Retourne les formsemestres filtrés par Retourne les formsemestres filtrés par
@ -115,8 +113,7 @@ def formsemestres_query():
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def bulletins(formsemestre_id: int): def bulletins(formsemestre_id: int):
""" """
Retourne les bulletins d'un formsemestre donné Retourne les bulletins d'un formsemestre donné
@ -140,8 +137,7 @@ def bulletins(formsemestre_id: int):
"/formsemestre/<int:formsemestre_id>/programme", "/formsemestre/<int:formsemestre_id>/programme",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_programme(formsemestre_id: int): def formsemestre_programme(formsemestre_id: int):
""" """
Retourne la liste des Ues, ressources et SAE d'un semestre Retourne la liste des Ues, ressources et SAE d'un semestre
@ -242,8 +238,7 @@ def formsemestre_programme(formsemestre_id: int):
methods=["GET"], methods=["GET"],
defaults={"etat": scu.DEF}, defaults={"etat": scu.DEF},
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_etudiants(formsemestre_id: int, etat: str): def formsemestre_etudiants(formsemestre_id: int, etat: str):
""" """
Retourne la liste des étudiants d'un formsemestre Retourne la liste des étudiants d'un formsemestre
@ -265,8 +260,7 @@ def formsemestre_etudiants(formsemestre_id: int, etat: str):
@bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etat_evals(formsemestre_id: int): def etat_evals(formsemestre_id: int):
""" """
Informations sur l'état des évaluations d'un formsemestre. Informations sur l'état des évaluations d'un formsemestre.
@ -372,8 +366,7 @@ def etat_evals(formsemestre_id: int):
@bp.route("/formsemestre/<int:formsemestre_id>/resultats", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/resultats", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_resultat(formsemestre_id: int): def formsemestre_resultat(formsemestre_id: int):
"""Tableau récapitulatif des résultats """Tableau récapitulatif des résultats
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules. Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules.

View File

@ -4,7 +4,7 @@
# from app import models # from app import models
# from app.api import bp # from app.api import bp
# from app.api.errors import error_response # from app.api.errors import error_response
# from app.api.auth import token_auth, token_permission_required # from app.api.auth import permission_required_api
# from app.scodoc.sco_prepajury import feuille_preparation_jury # from app.scodoc.sco_prepajury import feuille_preparation_jury
# from app.scodoc.sco_pvjury import formsemestre_pvjury # from app.scodoc.sco_pvjury import formsemestre_pvjury

View File

@ -38,13 +38,12 @@ from app.api.auth import token_auth
from app.api.errors import error_response from app.api.errors import error_response
from app.models import Departement from app.models import Departement
from app.scodoc.sco_logos import list_logos, find_logo from app.scodoc.sco_logos import list_logos, find_logo
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/logos", methods=["GET"]) @bp.route("/logos", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def api_get_glob_logos(): def api_get_glob_logos():
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None): if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
return error_response(401, message="accès interdit") return error_response(401, message="accès interdit")
@ -56,8 +55,7 @@ def api_get_glob_logos():
@bp.route("/logos/<string:logoname>", methods=["GET"]) @bp.route("/logos/<string:logoname>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def api_get_glob_logo(logoname): def api_get_glob_logo(logoname):
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None): if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
return error_response(401, message="accès interdit") return error_response(401, message="accès interdit")
@ -73,8 +71,7 @@ def api_get_glob_logo(logoname):
@bp.route("/departements/<string:departement>/logos", methods=["GET"]) @bp.route("/departements/<string:departement>/logos", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def api_get_local_logos(departement): def api_get_local_logos(departement):
dept_id = Departement.from_acronym(departement).id dept_id = Departement.from_acronym(departement).id
if not g.current_user.has_permission(Permission.ScoChangePreferences, departement): if not g.current_user.has_permission(Permission.ScoChangePreferences, departement):
@ -84,8 +81,7 @@ def api_get_local_logos(departement):
@bp.route("/departements/<string:departement>/logos/<string:logoname>", methods=["GET"]) @bp.route("/departements/<string:departement>/logos/<string:logoname>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def api_get_local_logo(departement, logoname): def api_get_local_logo(departement, logoname):
# format = requested_format("jpg", ['png', 'jpg']) XXX ? # format = requested_format("jpg", ['png', 'jpg']) XXX ?
dept_id = Departement.from_acronym(departement).id dept_id = Departement.from_acronym(departement).id

View File

@ -12,7 +12,7 @@ from flask import abort, jsonify, request
import app import app
from app import db, log from app import db, log
from app.api import bp from app.api import bp
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import FormSemestre, FormSemestreInscription, Identite from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import GroupDescr, Partition from app.models import GroupDescr, Partition
from app.models.groups import group_membership from app.models.groups import group_membership
@ -22,8 +22,7 @@ from app.scodoc import sco_utils as scu
@bp.route("/partition/<int:partition_id>", methods=["GET"]) @bp.route("/partition/<int:partition_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def partition_info(partition_id: int): def partition_info(partition_id: int):
""" """
Exemple de résultat : Exemple de résultat :
@ -48,8 +47,7 @@ def partition_info(partition_id: int):
@bp.route("/formsemestre/<int:formsemestre_id>/partitions", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/partitions", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_partitions(formsemestre_id: int): def formsemestre_partitions(formsemestre_id: int):
""" """
Retourne la liste de toutes les partitions d'un formsemestre Retourne la liste de toutes les partitions d'un formsemestre
@ -64,8 +62,7 @@ def formsemestre_partitions(formsemestre_id: int):
@bp.route("/group/<int:group_id>/etudiants", methods=["GET"]) @bp.route("/group/<int:group_id>/etudiants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etud_in_group(group_id: int): def etud_in_group(group_id: int):
""" """
Retourne la liste des étudiants dans un groupe Retourne la liste des étudiants dans un groupe
@ -91,8 +88,7 @@ def etud_in_group(group_id: int):
@bp.route("/group/<int:group_id>/etudiants/query", methods=["GET"]) @bp.route("/group/<int:group_id>/etudiants/query", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etud_in_group_query(group_id: int): def etud_in_group_query(group_id: int):
"""Etudiants du groupe, filtrés par état""" """Etudiants du groupe, filtrés par état"""
etat = request.args.get("etat") etat = request.args.get("etat")
@ -110,8 +106,7 @@ def etud_in_group_query(group_id: int):
@bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"]) @bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def set_etud_group(etudid: int, group_id: int): def set_etud_group(etudid: int, group_id: int):
"""Affecte l'étudiant au groupe indiqué""" """Affecte l'étudiant au groupe indiqué"""
etud = Identite.query.get_or_404(etudid) etud = Identite.query.get_or_404(etudid)
@ -136,8 +131,7 @@ def set_etud_group(etudid: int, group_id: int):
@bp.route("/partition/<int:partition_id>/group/create", methods=["POST"]) @bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def group_create(partition_id: int): def group_create(partition_id: int):
"""Création d'un groupe dans une partition """Création d'un groupe dans une partition
@ -167,8 +161,7 @@ def group_create(partition_id: int):
@bp.route("/group/<int:group_id>/delete", methods=["POST"]) @bp.route("/group/<int:group_id>/delete", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def group_delete(group_id: int): def group_delete(group_id: int):
"""Suppression d'un groupe""" """Suppression d'un groupe"""
group = GroupDescr.query.get_or_404(group_id) group = GroupDescr.query.get_or_404(group_id)
@ -184,8 +177,7 @@ def group_delete(group_id: int):
@bp.route("/group/<int:group_id>/edit", methods=["POST"]) @bp.route("/group/<int:group_id>/edit", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def group_edit(group_id: int): def group_edit(group_id: int):
"""Edit a group""" """Edit a group"""
group: GroupDescr = GroupDescr.query.get_or_404(group_id) group: GroupDescr = GroupDescr.query.get_or_404(group_id)
@ -206,8 +198,7 @@ def group_edit(group_id: int):
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"]) @bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def partition_create(formsemestre_id: int): def partition_create(formsemestre_id: int):
"""Création d'une partition dans un semestre """Création d'une partition dans un semestre
@ -253,8 +244,7 @@ def partition_create(formsemestre_id: int):
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"]) @bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def partition_edit(partition_id: int): def partition_edit(partition_id: int):
"""Modification d'une partition dans un semestre """Modification d'une partition dans un semestre
@ -306,8 +296,7 @@ def partition_edit(partition_id: int):
@bp.route("/partition/<int:partition_id>/delete", methods=["POST"]) @bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
@token_auth.login_required @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
@token_permission_required(Permission.APIEditGroups)
def partition_delete(partition_id: int): def partition_delete(partition_id: int):
"""Suppression d'une partition (et de tous ses groupes). """Suppression d'une partition (et de tous ses groupes).

View File

@ -36,8 +36,13 @@ load_dotenv(os.path.join(BASEDIR, ".env"))
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False))) CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000" SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000"
API_URL = SCODOC_URL + "/ScoDoc/api" API_URL = SCODOC_URL + "/ScoDoc/api"
# Admin:
SCODOC_USER = os.environ["SCODOC_USER"] SCODOC_USER = os.environ["SCODOC_USER"]
SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"] SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"]
# Lecteur
SCODOC_USER_API_LECTEUR = os.environ["SCODOC_USER_API_LECTEUR"]
SCODOC_PASSWORD_API_LECTEUR = os.environ["SCODOC_PASSWORD_API_LECTEUR"]
print(f"SCODOC_URL={SCODOC_URL}") print(f"SCODOC_URL={SCODOC_URL}")
print(f"API URL={API_URL}") print(f"API URL={API_URL}")
@ -84,13 +89,16 @@ def POST_JSON(path: str, data: dict = {}, headers={}, errmsg=None):
return r.json() # decode la reponse JSON return r.json() # decode la reponse JSON
# --- Obtention du jeton (token) def GET_TOKEN(user, password):
r = requests.post(API_URL + "/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)) "Obtention du jeton (token)"
assert r.status_code == 200 r = requests.post(API_URL + "/tokens", auth=(user, password))
token = r.json()["token"] assert r.status_code == 200
HEADERS = {"Authorization": f"Bearer {token}"} token = r.json()["token"]
# HEADERS_JSON = HEADERS.copy() return {"Authorization": f"Bearer {token}"}
# HEADERS_JSON["Content-Type"] = "application/json"
HEADERS = GET_TOKEN(SCODOC_USER, SCODOC_PASSWORD)
HEADERS_USER = GET_TOKEN(SCODOC_USER_API_LECTEUR, SCODOC_PASSWORD_API_LECTEUR)
r = requests.get(API_URL + "/departements", headers=HEADERS, verify=CHK_CERT) r = requests.get(API_URL + "/departements", headers=HEADERS, verify=CHK_CERT)
if r.status_code != 200: if r.status_code != 200: