Connecteur NextCloud et chaîne d'édition en ligne
Cette page décrit l’architecture et le fonctionnement des composants
qui étendent l’espace documentaire
de l’ENT :
- le connecteur Nextcloud pour la synchronisation des fichiers,
- le connecteur
loolpour l’intégration WOPI
Ce que voit l'utilisateur : Espace documentaire.
Vue d'ensemble
Trois chemins d'accès à un fichier coexistent, avec des stockages différents :
| Chemin | Où est le fichier | Module ENT | Protocole |
|---|---|---|---|
| Espace documentaire natif | storage de l'ENT (gridfs/disque) | workspace (entcore) | interne |
| Documents synchronisés | serveur NextCloud | fr.openent~nextcloud | OCS + WebDAV |
| Édition en ligne d'un document natif | storage de l'ENT | fr.openent~lool | WOPI |
| Édition en ligne d'un document synchronisé | serveur NextCloud | app NextCloud richdocuments | WOPI (interne à NextCloud) |
Le point à retenir : c'est le serveur d'édition qui rappelle l'ENT (server-to-server) pour lire
et écrire le document. L'adresse par laquelle il rappelle — le WOPISrc — doit être explicitement
autorisée côté Collabora, faute de quoi le document refuse de s'ouvrir.
Activation / désactivation
Deux interrupteurs indépendants, tous deux dans le bloc config du module workspace de
ent-core.yaml (même fichier utilisé en développement et poussé en production via le chart Helm) :
| Clé | Contrôle | Effet si false / vide |
|---|---|---|
enable-nextcloud | l'entrée « Documents synchronisés » dans l'arbre de l'espace documentaire (workspace.html, variable ENABLE_NEXTCLOUD injectée côté serveur) | l'entrée disparaît de l'espace documentaire lui-même |
publicConf.folder-service | la liste des fournisseurs proposés par le picker réutilisable (directive virtual-media-library, utilisé pour joindre une pièce depuis diary, edt, calendar…) | retirer nextcloud de la liste masque NextCloud dans ce picker |
- name: org.entcore~workspace~${ENTCORE_VERSION}-patched
config:
enable-nextcloud: false
publicConf:
folder-service: [] # retirer "nextcloud" de la liste ; laisser les autres fournisseurs
Les deux clés sont indépendantes : n'en changer qu'une laisse NextCloud visible dans un des deux
écrans. folder-service est une liste statique (pas de condition possible dans le YAML) — elle
n'est pas recalculée à partir de enable-nextcloud, il faut l'éditer explicitement en miroir.
Désactiver ces deux clés ne retire pas le connecteur : le module fr.openent~nextcloud reste
déclaré et ses routes /nextcloud/* répondent toujours. Un utilisateur sans droit qui accède
directement à une URL du module obtient la page d'erreur générique 401 (e401.page), pas une 404 —
c'est attendu (comportement identique à n'importe quel module ENT sans droit attribué), pas un bug.
Pour retirer le connecteur lui-même (plus aucune route, y compris ce 401), il faut supprimer le bloc
- name: fr.openent~nextcloud~${...} d'ent-core.yaml.
Côté Helm, nextcloud.enabled (values.yaml) est un troisième réglage, sans rapport avec les
deux précédents : il pilote uniquement le déploiement de l'infrastructure NextCloud interne au
chart (pod, base de données, Collabora) — utile si l'ENT ne se connecte pas à un serveur NextCloud
externe. Il n'a aucun effet sur la visibilité de la fonctionnalité dans l'interface.
Connecteur NextCloud (fr.openent~nextcloud)
Module Vert.x, port 8060, chemin /nextcloud. Il ne stocke aucun fichier : il agit comme
mandataire authentifié entre l'utilisateur ENT et un serveur NextCloud.
Configuration (ent-core.yaml)
| Clé | Rôle |
|---|---|
nextcloud-host | URL du serveur NextCloud (variable NEXTCLOUD_HOST) |
admin-credential | compte d'administration NextCloud, utilisé pour le provisioning OCS |
endpoint.ocs-api / endpoint.webdav-api | préfixes d'API (/ocs/v1.php, /remote.php/dav/files) |
quota | quota appliqué aux comptes créés |
is-nextcloud-url-hidden | masque l'URL NextCloud dans l'interface |
nextcloud-providers | une entrée par host ENT (voir ci-dessous) |
db-schema: nextcloud | schéma PostgreSQL du connecteur |
nextcloud-providers mérite une explication : le connecteur résout sa configuration par host de
la requête (nextcloudConfigMapByHost). Sans entrée correspondant au host reçu, la résolution
renvoie null et la requête échoue en 500 sans trace. Tout alias par lequel l'ENT peut être joint
doit donc y figurer.
Comptes et sessions
Le connecteur crée le compte NextCloud à la volée puis conserve un mot de passe d'application dans PostgreSQL :
CREATE TABLE nextcloud.user (
id bigserial NOT NULL,
user_id varchar(255) NOT NULL, -- identifiant ENT
username varchar(255) NOT NULL, -- identifiant NextCloud
password varchar(255) NOT NULL, -- jeton de session applicatif
last_modified timestamp DEFAULT now(),
PRIMARY KEY (user_id)
);
DefaultUserService.provideUserSession vérifie la validité du jeton stocké ; s'il est périmé, il
régénère une session (changeUserPassword + OCS) ; si le compte n'existe pas encore, il le crée
(/cloud/users). L'utilisateur ne saisit jamais de mot de passe NextCloud.
Principaux points d'entrée
| Route | Usage |
|---|---|
GET /nextcloud/files/user/:userid | lister un dossier (WebDAV PROPFIND) |
PUT /nextcloud/files/user/:userid/upload | déposer un fichier |
GET /nextcloud/files/user/:userid/edit | ouvrir en édition (Direct Editing NextCloud) |
POST /nextcloud/files/user/:userid/share | partage nominatif (OCS files_sharing) |
POST /nextcloud/files/user/:userid/create/document | créer un document Office vierge (docx/xlsx/pptx) à partir d'un modèle |
PUT …/workspace/move/cloud, PUT …/move/workspace | transferts espace ENT ↔ espace synchronisé |
GET/PUT /nextcloud/desktop/config[/structure/:id] | réglages nationaux / par établissement |
GET /nextcloud/admin/share-structures | partage inter-établissements |
create/document fonctionne comme la création côté lool (mêmes modèles vierges docx/xlsx/
pptx, réutilisés depuis public/nextcloud-templates/) : le fichier modèle est stocké
temporairement dans le Storage de l'ENT (storage.writeBuffer), puis déposé sur NextCloud via
WebDAV (uploadFile, deleteFromStorage=true — pas de copie durable côté ENT). Le type de fichier
est validé contre une liste blanche stricte (docx/xlsx/pptx) côté contrôleur avant toute
lecture disque, type alimentant directement le chemin du modèle.
La résolution établissement > national > défaut (extensions bloquées, nom du dossier synchronisé, bande passante) est décrite dans Détails techniques — Espace documentaire.
Connecteur lool (WOPI direct)
Module Vert.x, port 8333, chemin /lool. Il permet d'éditer dans le navigateur un document
resté dans le storage de l'ENT : aucun stockage externe, aucune copie côté NextCloud.
Configuration
- name: fr.openent~lool~${LOOL_VERSION}
config:
port: 8333
app-address: /lool
wopi:
provider:
type: ${LOOL_WOPI_TYPE} # LibreOfficeOnline | OnlyOffice
url: ${LOOL_WOPI_URL} # URL du serveur d'édition
hour-duration-token: 10
templates: [docx, pptx, xlsx]
templates pilote les modèles proposés à la création. Le défaut du fournisseur
LibreOfficeOnline ([odt, odp, ods]) laisse les cartes Word/PowerPoint/Excel non
sélectionnables côté interface, qui ne reconnaît que docx/pptx/xlsx.
Côté workspace, l'affichage dépend de deux conditions : enable-lool: true dans la configuration,
et le droit workflow de l'utilisateur.
Déroulé d'une ouverture
- L'utilisateur sélectionne un document et clique Éditer →
GET /lool/documents/:id/open. - Le connecteur génère un jeton WOPI (Mongo
wopi_token, duréehour-duration-token), lit l'urlsrccorrespondant au type de fichier dans la collectionlool_discover, et rend la vuedoc.htmlqui porte l'iframe de l'éditeur. - Le serveur d'édition rappelle le connecteur avec le jeton :
GET /lool/wopi/files/:id(métadonnées),GET …/contents(lecture),POST …/contents(enregistrement). - À la fermeture, le jeton est révoqué (
DELETE /lool/wopi/documents/:id/tokens/:token, ouPOSTdu même chemin viasendBeacon).
La collection lool_discover est alimentée au démarrage par un GET /hosting/discovery sur le
serveur d'édition. Cet appel est serveur→serveur, mais LOOL_WOPI_URL doit malgré tout porter
l'URL publique de Collabora : la même variable compose le frame-src de la
content-security-policy (ent-core.yaml), et l'iframe de l'éditeur pointe sur le host public issu
de l'attribut urlsrc du discovery. Avec une URL interne, le discovery aboutit — capacités
remontées, route en 200 — mais le navigateur refuse le cadre :
Framing 'https://collabora…' violates the Content Security Policy directive: "frame-src …"
Le symptôme est trompeur : page de l'éditeur servie normalement, aucune erreur serveur, et un cadre vide.
Points d'entrée
| Route | Droit | Usage |
|---|---|---|
GET /lool | workflow view | page du module |
GET /lool/modal/create | authentifié | fenêtre de création (iframe ouverte par le workspace sur #/lool) |
GET /lool/document · POST /lool/document | workflow create.document | créer un document bureautique |
GET /lool/documents/:id/open | workflow open.file | ouvrir un document dans l'éditeur |
GET /lool/providers/context | authentifié | fournisseur, capacités issues du discovery, modèles |
GET /lool/discover | super-administrateur | relancer le discovery |
GET/POST /lool/wopi/files/:id[/contents] | jeton WOPI | dialogue avec le serveur d'édition |
Droits
Le module déclare quatre actions (view, open.file, create.document, monitoring) et trois
rôles (Lecture, Gestion, Administration). Comme pour tout module ENT, ces rôles doivent être
attribués à des groupes de profils dans la console d'administration : tant qu'aucun groupe
n'est rattaché, le bouton Éditer n'apparaît pas dans l'espace documentaire, alors même que le
connecteur répond correctement.
Serveur d'édition (Collabora)
coolwsd n'accepte de servir un document que si l'hôte qui a émis le WOPISrc figure dans un de
ses aliasgroupN. Deux appelants coexistent donc :
| Variable | Hôtes attendus | Émetteur du WOPISrc |
|---|---|---|
aliasgroup1 | hosts NextCloud (public + service interne) | app richdocuments de NextCloud |
aliasgroup2 | hosts de l'ENT | connecteur lool |
Chaque variable vaut une liste d'URLs scheme://host séparées par des virgules : la première est
l'hôte du groupe, les suivantes ses alias. coolwsd en extrait le nom d'hôte en analysant
l'URL, puis compare ce nom d'hôte à l'entrée prise comme expression régulière — les points sont
donc échappés, mais le schéma doit rester littéral. Un https?:// (tentant, pour couvrir les deux
protocoles) empêche l'analyse : plus aucune règle n'est enregistrée.
Un alias manquant — ou rendu illisible de cette façon — produit « Hôte WOPI non autorisé » côté éditeur, avec dans le journal de Collabora :
No authorized hosts found matching the target host [www.exemple.fr] in config
Le même écran apparaît sur une simple erreur réseau, d'où la confusion fréquente : c'est le journal qui tranche.
Déploiement Kubernetes
Le chart openent-monolithe déploie NextCloud, MariaDB, Collabora (et éventuellement OnlyOffice)
et câble l'ensemble automatiquement :
| Réglage | Effet |
|---|---|
nextcloud.enabled | déploie la brique et injecte NEXTCLOUD_HOST / identifiants admin dans le launcher |
nextcloud.collabora.expose | path (sous le host NextCloud) ou host (domaine dédié) |
nextcloud.collabora.entAliases | hosts de l'ENT pour aliasgroup2 — vide = tous les ingress.hosts |
nextcloud.collabora.allowEntWopi | false réserve Collabora à NextCloud (pas d'aliasgroup2) |
nextcloud.collabora.loolProviderUrl | valeur de LOOL_WOPI_URL — vide = URL publique de Collabora (requis par le frame-src) |
Le routage /lool (port 8333) et /nextcloud (port 8060) est déclaré dans service.modules, qui
génère à la fois les ports du Service et les règles d'Ingress pour chaque domaine.
En développement, dev/nextcloud/docker-compose.yml reproduit la même topologie : un nginx place
NextCloud et Collabora derrière une URL unique, et Collabora tourne en network_mode: host pour
que localhost désigne la même machine dans le conteneur et dans le navigateur.
Diagnostic
| Symptôme | Cause probable | Vérification |
|---|---|---|
/lool/... en 404 ou 502 | module non routé (Ingress/Service) ou non démarré | GET /lool/providers/context doit répondre 200 |
Capacités vides dans providers/context | discovery en échec (serveur d'édition injoignable) | journal du module : LibreOfficeOnline discover wopi1 OK |
| « Hôte WOPI non autorisé » | aliasgroup incomplet ou schéma non analysable | journal Collabora : « No authorized hosts found matching… » |
| Bouton Éditer absent | droit workflow lool.openFile non attribué | console d'administration, rôles lool |
| Éditeur ouvert mais cadre vide | frame-src de la CSP sans le host public de Collabora (LOOL_WOPI_URL interne) | console du navigateur : « violates … frame-src » |
500 sans trace sur /nextcloud/... | host absent de nextcloud-providers | configuration du module |
| Documents synchronisés vides | session NextCloud périmée ou compte non provisionné | table nextcloud.user |
Couverture de tests
Scénarios e2e du dossier apps/open-ent-e2e/src/modules/12_espace_documentaire couvrant ces
briques :
06_documents_synchronises_acces.spec.ts— accès à l'espace synchronisé (connecteur NextCloud) ;07_partage_nominatif.spec.ts— partage OCS entre deux comptes ENT ;08_edition_en_ligne.spec.ts— édition d'un document stocké dans NextCloud (Direct Editing) ;09_blocage_extensions_dangereuses.spec.ts— refus des extensions interdites ;10_edition_document_natif_lool.spec.ts— connecteurlool: fournisseur WOPI joignable, discovery abouti, ouverture d'un document natif de l'ENT dans l'éditeur.11_creation_document_nextcloud.spec.ts— bouton « Créer un document » : création d'un docx à partir d'un modèle, ouverture immédiate dans l'éditeur en ligne.
06, 08 et 11 dépendent du nœud « Documents synchronisés » dans l'arbre du workspace. Ce nœud
est invisible en session Playwright fraîche (jamais utilisée manuellement au préalable pour le
même compte), alors qu'il fonctionne normalement en navigateur réel — cause non identifiée à ce
jour (registre applicatif ? préférence utilisateur initialisée seulement après une première vraie
interaction ?). Les trois tests skippent proprement (pas d'échec) plutôt que d'échouer dans ce
cas ; 11_creation_document_nextcloud.spec.ts a été validé manuellement (backend + interface)
faute de pouvoir produire les captures automatiques tant que ce point n'est pas résolu.