Skip to Content

Chapitre 9.1 — Architecture des web services

Partie 9 : Web Services — Chapitre 1/5 Public : dev Next.js/React. On pose le modèle avant les tokens (9.2) et le code (9.4). Version de référence : Moodle 5.2 (external functions via \core_external\external_api, protocole REST/JSON).

⏱️ TL;DR

  • Une external function est une fonction serveur typée (paramètres + retour déclarés) et sécurisée (login, contexte, capabilities). C’est l’unité de base de l’API.
  • Un external service est un regroupement de fonctions exposé sous un token. Deux types : built-in (le service mobile) ou custom (créé par l’admin).
  • Un protocole transporte l’appel : REST (JSON, le plus courant) principalement ; d’autres existent historiquement.
  • Fait clé : les external functions sont le même code qui sert l’AJAX interne (ch. 7.3, core/ajax) et l’API externe. Une fonction, deux consommateurs.
  • Flux d’un appel : client + token → endpoint (/webservice/rest/server.php) → Moodle authentifie le token (→ un utilisateur), vérifie que la fonction est dans le service, valide les paramètres (types), exécute (avec les permissions de cet utilisateur), renvoie une réponse typée en JSON.
  • Activer : Administration → Serveur → Web services — activer les WS, activer le protocole REST, créer un service, y ajouter des fonctions, générer un token.
  • API utilisateur vs système : un token porte l’identité d’un utilisateur ; les appels s’exécutent avec ses permissions. Un « compte de service » est juste un utilisateur dédié aux intégrations.

🎯 Objectifs

  • Distinguer external function, external service, protocole, token.
  • Comprendre que les WS et l’AJAX interne partagent le même code.
  • Décrire le flux complet d’un appel REST.
  • Activer les web services et le protocole REST.
  • Saisir le modèle « token = identité + permissions ».

1. Le vocabulaire, sans ambiguïté

Quatre termes qu’on confond souvent :

TermeC’est quoiAnalogie
External functionUne fonction serveur typée+sécurisée (ex. core_course_get_courses)une route API typée
External serviceUn groupe de fonctions autorisées, exposé via tokenun plan/scope d’API
ProtocoleLe transport de l’appel (REST/JSON surtout)HTTP/REST vs GraphQL
TokenUne clé liée à un utilisateur + un serviceune API key portant une identité

L’articulation : vous créez (ou utilisez) un service qui liste des fonctions ; un token donne accès à ce service au nom d’un utilisateur ; le client appelle une fonction via un protocole (REST).


2. Le fait le plus important : une fonction, deux consommateurs

Rappel du chapitre 7.3 : core/ajax appelle des external functions pour l’interface interne de Moodle. Ce sont exactement les mêmes fonctions que l’API externe expose. Une external function bien écrite sert :

  • L’AJAX interne (le JS de Moodle, via core/ajax, avec la session en cours).
  • L’API externe (curl, Next.js, un SIRH, via un token).

Conséquence de conception majeure : quand vous écrivez une external function (ch. 9.4), vous créez à la fois un endpoint pour votre JS et un point d’intégration externe. Vous n’écrivez pas deux fois la logique. C’est pourquoi Moodle centralise toute la logique d’accès (validation, capabilities) dans la fonction, indépendamment de qui l’appelle.

💡 Pour un dev React : imaginez une couche de route handlers typés (à la tRPC) qui sert et votre frontend interne et vos partenaires externes, avec la même validation et les mêmes contrôles d’autorisation. Vous ne dupliquez pas « API interne » et « API publique » : une seule surface, deux modes d’authentification (session pour l’interne, token pour l’externe).


3. External service : built-in vs custom

Un service regroupe les fonctions accessibles via un token. Deux origines :

  • Services intégrés (built-in) : fournis par le core ou des plugins via db/services.php. Le plus connu : le service mobile (moodle_mobile_app), qui expose les fonctions utilisées par l’app officielle.
  • Services custom : créés par l’admin dans l’interface (Web services → Services externes), en y ajoutant à la main les fonctions autorisées. C’est ce qu’on fait pour une intégration Next.js : un service dédié, ne contenant que les fonctions dont l’app a besoin.

Un service porte aussi des options : restreindre aux utilisateurs autorisés, activer/désactiver, exiger des capabilities. Le principe de moindre privilège s’applique : un service d’intégration ne doit exposer que le strict nécessaire.

⚠️ Piège : ne donnez jamais à un service d’intégration l’accès à des fonctions dont il n’a pas besoin (surtout les fonctions d’écriture/administration). Chaque fonction ajoutée à un service est une capacité offerte au porteur du token. Un service minimal = une surface d’attaque minimale.


4. Les protocoles : REST en tête

Moodle peut exposer les web services via plusieurs protocoles de transport. En pratique, REST (JSON) domine et c’est celui qu’on utilise partout dans cette partie :

  • REST : endpoint /webservice/rest/server.php, paramètres en POST (ou GET), réponse JSON (par défaut) ou XML. Simple, universel, parfait pour Next.js/fetch.
  • D’autres protocoles ont existé historiquement (SOAP, XML-RPC) — ignorez-les pour une intégration moderne, activez REST.

L’endpoint REST attend au minimum :

ParamètreRôle
wstokenle token (identité + service)
wsfunctionle nom de la fonction (ex. core_course_get_courses)
moodlewsrestformatjson (recommandé) ou xml
les args de la fonctionselon sa signature

⚠️ Piège REST n°1 (à retenir dès maintenant) : le protocole REST de Moodle renvoie souvent HTTP 200 même en cas d’erreur applicative (token invalide, capability manquante, exception). L’erreur est dans le corps JSON ({"exception": ..., "errorcode": ..., "message": ...}). Ne vous fiez donc pas au seul code HTTP : inspectez toujours le payload. Ce piège est développé au chapitre 5 (client Next.js).


5. Le flux d’un appel, étape par étape

Reprenons le diagramme du §1 en détaillant ce que Moodle fait à chaque étape (c’est exactement ce que vous implémenterez au ch. 9.4) :

  1. Réception : le client POST sur /webservice/rest/server.php avec wstoken, wsfunction, les args, et moodlewsrestformat=json.
  2. Authentification du token : Moodle résout le token → l’utilisateur associé et le service. Token invalide/expiré → erreur (dans le corps).
  3. Autorisation du service : la fonction demandée est-elle dans ce service ? L’utilisateur est-il autorisé à l’utiliser ? Sinon → erreur.
  4. Validation des paramètres : Moodle applique la déclaration execute_parameters() de la fonction — chaque paramètre est typé (PARAM_INT, structures…) et nettoyé. Un paramètre malformé → erreur avant toute logique.
  5. Exécution : la méthode execute() tourne avec les permissions de l’utilisateur du token. Elle refait ses propres contrôles (require_capability, validation de contexte) — la sécurité n’est jamais déléguée au seul service.
  6. Formatage du retour : execute_returns() décrit la forme du résultat ; Moodle la valide et sérialise en JSON.
  7. Réponse : JSON renvoyé (ou payload d’erreur, en 200).

Le point crucial : la fonction s’exécute avec l’identité et les permissions du porteur du token. Un token d’un enseignant ne peut faire que ce que l’enseignant peut faire. Il n’y a pas de « super-pouvoir » du web service au-delà des permissions de l’utilisateur — c’est le même modèle capabilities/contextes que partout (Partie 2).


6. Activer les web services (côté admin)

Les web services sont désactivés par défaut. Pour une intégration, l’admin :

  1. Active les web services : Administration → Fonctions avancées → enablewebservices.
  2. Active le protocole REST : Serveur → Web services → Gérer les protocoles → activer REST.
  3. Crée un service custom : Serveur → Web services → Services externes → « Ajouter », nommer (ex. « Intégration Next.js »), cocher « Activé ».
  4. Ajoute les fonctions au service (uniquement celles nécessaires).
  5. Crée un utilisateur de service (recommandé) et lui génère un token pour ce service (ch. 9.2).
  6. Vérifie les capabilities : l’utilisateur doit avoir webservice/rest:use et les capabilities de chaque fonction (ch. 9.2).

Il existe un assistant (« Documentation de l’API » et « API tester ») et une page « Vue d’ensemble » qui déroule ces étapes. La documentation de l’API intégrée (par service) liste chaque fonction avec sa signature exacte — votre référence de contrat (ch. 9.3).

💡 Pour un dev React : cette configuration ressemble à créer une API key scoping dans un dashboard SaaS : vous définissez un « scope » (le service = les endpoints autorisés), attachez une identité (l’utilisateur de service), générez une clé (le token) et vérifiez les permissions. La différence : les permissions ne sont pas dans le service mais dans le système de capabilities de l’utilisateur (Partie 2).


7. API « utilisateur » vs « système »

On parle parfois d’API utilisateur et d’API système. Ce n’est pas deux APIs différentes, mais deux usages du même mécanisme :

  • Token par utilisateur final : chaque utilisateur a son token ; les appels s’exécutent en son nom (ses cours, ses notes). Typique d’une app où chaque personne agit pour elle-même (ex. l’app mobile : un token par utilisateur connecté).
  • Compte de service (« système ») : un utilisateur dédié aux intégrations (ex. svc_integration), avec des capabilities précises, dont un seul token sert le backend. Typique d’un SIRH qui inscrit des utilisateurs en masse : il agit en tant que ce compte de service, pas en tant qu’un étudiant.

Dans les deux cas, tout passe par un utilisateur et ses permissions. Le choix dépend de « au nom de qui l’intégration agit ». Pour une app Next.js multi-utilisateurs, on combine souvent : un compte de service pour les opérations d’administration, et/ou l’obtention de tokens par utilisateur pour les actions personnelles (ch. 9.2 et 9.5).

⚠️ Piège de conception : n’utilisez pas un compte admin comme compte de service « par facilité ». Un token d’admin peut tout faire ; s’il fuit, c’est la compromission totale. Créez un utilisateur dédié avec exactement les capabilities nécessaires (moindre privilège). C’est le sujet du chapitre 2.


8. Synthèse

Les web services de Moodle = des fonctions typées et sécurisées (les mêmes que l’interne), groupées en services, accessibles par token (portant une identité) via REST. Toute la sécurité repose sur les capabilities de l’utilisateur du token. Les chapitres suivants approfondissent : les tokens et la sécurité (9.2), les fonctions du core (9.3), écrire les vôtres (9.4), et consommer depuis Next.js (9.5).


✏️ Exercices

Exercice 1 — Vocabulaire. Associez : (a) core_enrol_get_users_courses ; (b) « Intégration Next.js » créé par l’admin ; (c) wstoken=abc123 ; (d) /webservice/rest/server.php. → external function / external service / token / protocole.

✅ Solution

(a) external function (une fonction précise) ; (b) external service (un groupe de fonctions autorisées) ; (c) token (clé liée à un utilisateur + service) ; (d) protocole (l’endpoint REST). Articulation : le token (c) donne accès au service (b) qui contient la fonction (a), appelée via le protocole (d).

Exercice 2 — Une fonction, deux appelants. Expliquez pourquoi écrire une external function pour votre plugin sert aussi votre JavaScript interne.

✅ Solution

Parce que les external functions sont le même code consommé par core/ajax (le JS interne, avec la session, ch. 7.3) et par l’API externe (curl/Next.js, avec un token). Une fois la fonction écrite (avec sa validation de paramètres et ses contrôles de capabilities), elle est appelable des deux côtés sans duplication. C’est pourquoi la logique d’accès (login, contexte, capabilities) doit vivre dans la fonction, pas dépendre de l’appelant.

Exercice 3 — Le piège du 200. Un dev teste un appel REST, reçoit HTTP 200, et conclut que tout va bien — mais rien n’est créé côté Moodle. Que s’est-il probablement passé ?

✅ Solution

Le protocole REST de Moodle renvoie souvent 200 même en cas d’erreur (token invalide, capability manquante, exception métier). L’erreur est dans le corps JSON ({"exception": ..., "errorcode": ..., "message": ...}). Le dev a regardé le code HTTP et non le payload. Correctif : toujours parser le corps et détecter la présence d’un champ exception/errorcode avant de considérer l’appel réussi (détaillé au ch. 9.5).

Exercice 4 — Moindre privilège. Une intégration Next.js doit seulement lister les cours d’un utilisateur. Quel service configurez-vous, et que refusez-vous ?

✅ Solution

Un service custom dédié contenant uniquement la (les) fonction(s) nécessaire(s) (ex. core_enrol_get_users_courses), attaché à un utilisateur de service ayant juste les capabilities requises + webservice/rest:use. On refuse d’y ajouter des fonctions d’écriture/administration (inscription, suppression, gestion d’utilisateurs) dont l’app n’a pas besoin, et on n’utilise pas un compte admin. Principe de moindre privilège : le service et l’utilisateur n’ont que le strict nécessaire.

Exercice 5 — Au nom de qui ? Distinguez : (a) une app mobile où chaque étudiant voit ses notes ; (b) un SIRH qui inscrit 500 employés chaque nuit. Quel modèle de token pour chacun ?

✅ Solution

(a) Token par utilisateur : chaque étudiant obtient son propre token (ou via login/token.php), les appels s’exécutent en son nom → il ne voit que ses notes (ses permissions). (b) Compte de service : un utilisateur dédié (svc_sirh) avec les capabilities d’inscription, un seul token utilisé par le backend du SIRH → il agit en tant que ce compte de service, pas en tant qu’un employé. Dans les deux cas, tout passe par un utilisateur et ses permissions ; le choix dépend de « au nom de qui » l’intégration agit.


🧠 Quiz de révision

Q1. Différenciez external function, external service et token.

Réponse

External function = une fonction serveur typée et sécurisée (ex. core_course_get_courses). External service = un groupe de fonctions autorisées, exposé via token. Token = une clé liée à un utilisateur et à un service, avec laquelle on appelle les fonctions.

Q2. Pourquoi dit-on qu’une external function a « deux consommateurs » ?

Réponse

Parce que le même code sert l’AJAX interne de Moodle (via core/ajax, avec la session) et l’API externe (via un token, curl/Next.js). Une fonction, deux modes d’authentification, pas de duplication de logique.

Q3. Quel piège majeur du protocole REST de Moodle faut-il retenir ?

Réponse

Il renvoie souvent HTTP 200 même en cas d’erreur ; l’erreur est dans le corps JSON (exception, errorcode, message). Il faut toujours inspecter le payload, pas seulement le code HTTP.

Q4. Décrivez brièvement le flux d’un appel REST.

Réponse

Client + token → endpoint REST → Moodle authentifie le token (→ utilisateur), vérifie que la fonction est dans le service, valide les paramètres (execute_parameters), exécute (execute, avec les permissions de l’utilisateur et ses propres require_capability), formate le retour (execute_returns), renvoie du JSON.

Q5. Avec quelles permissions une external function s’exécute-t-elle ?

Réponse

Avec les permissions de l’utilisateur associé au token (ses capabilities dans les contextes concernés). Il n’y a pas de super-pouvoir du web service : un token d’enseignant ne peut faire que ce que l’enseignant peut faire. D’où l’importance d’un compte de service à moindre privilège plutôt qu’un compte admin.

Chapitre suivant : 02 — Tokens, authentification et sécurité — obtenir un token (manuel, login/token.php, service mobile), token par utilisateur vs compte de service, restrictions (IP, expiration, fonctions autorisées), les capabilities requises, et les pièges de sécurité (un token = une identité, à ne jamais exposer côté client).