Skip to main content
Le partage implémente la RFC 9670 (urn:ietf:params:jmap:principals) : un annuaire de Principals (les personnes, équipes, ressources et lieux de l’organisation) et un modèle de droits uniforme que chaque type de collection réutilise.

Objets et méthodes

Le modèle de droits

Chaque collection partageable (Mailbox, Calendar, TaskList, AddressBook, FileNode, Label) porte la même carte shareWith : identifiant de principal vers objet de droits (mayRead, mayWrite, mayAdmin, plus des droits de domaine comme mayReadFreeBusy). Les droits sont :
  • Écrits par le propriétaire sur l’objet collection ; chaque changement de droit produit une ShareNotification pour le bénéficiaire et un changement enregistré pour la resynchronisation.
  • Exercés par l’indirection shared: : un bénéficiaire adresse les données du propriétaire avec l’identifiant de compte accordé dans le slot accountId JMAP standard ; l’objet accounts de la session liste ce qui est joignable. Les capacités se résolvent sur l’appelant authentifié, la résidence des données sur le propriétaire : l’usurpation n’est pas représentable dans la forme de la requête.
  • Fermés par défaut. Le dispatch des comptes partagés refuse par défaut ; une erreur de recherche de droits refuse plutôt que d’accorder.

Sémantique d’annuaire à connaître

  • Les types de principals sont honnêtes. Le principal d’un compte d’équipe partagé est type: "other" (non connectable), jamais individual : les sélecteurs de personnes peuvent se fier au type.
  • Les bénéficiaires doivent être adossés à un humain. Un droit accordé à un principal adossé à un compte non connectable est rejeté à l’écriture sur tous les domaines.
  • Détruire un principal purge ses droits. Un destroy de Principal/set retire les clés du bénéficiaire de chaque carte shareWith (pas d’entrées d’ACL pendantes) et avertit ou s’interrompt s’il laisserait un drive partagé sans gardien éligible.
  • L’effacement laisse des pierres tombales. Le principal d’une personne effacée est nettoyé de ses données personnelles mais conservé comme ancre de graphe, et un principal en pierre tombale est refusé comme nouveau bénéficiaire (conformité).
  • Principal/getAvailability (le libre/occupé) est documenté sur la page agenda.
ShareNotification/query renvoie un ordre total (du plus récent au plus ancien). Les règles de rigueur sont identiques à la surface courrier.

D’une organisation à l’autre : la fédération

Tout ce qui précède partage des données à l’intérieur d’une organisation. Partager avec une organisation différente est une surface distincte, urn:oximail:params:jmap:federation, et ce n’est volontairement pas le même mécanisme : un grant fédéré n’est ni une entrée shareWith, ni une ligne d’accès au compte. En v0.30.0, ce qu’il porte, c’est la disponibilité (le libre/occupé) : le grant élargit qui peut demander, jamais ce qui est visible. L’objet de capacité annonce le vocabulaire au lieu de laisser les clients le découvrir par des refus :

Le bénéficiaire est une organisation, pas une personne

supportedGranteeKinds vaut ["realm"], et ce n’est pas une question de calendrier. L’identité qu’une requête fédérée prouve aujourd’hui est le nœud émetteur, pas lequel de ses principals a agi. Un grant nommant une seule personne à l’autre bout ne serait donc pas réellement plus étroit qu’un grant nommant son organisation : il en aurait seulement l’air dans une interface. La liste existe pour que, le jour où une couche d’identité par principal arrivera, "principal" soit un ajout que le client peut détecter, et non un changement de comportement à deviner. granteeValue doit être un realm avec lequel vous avez réellement été appairé. L’appairage lui-même est une cérémonie d’opérateur bilatérale et hors bande : ce n’est pas une méthode JMAP, et c’est précisément pourquoi FederationPeer existe. Sans cette liste, un client n’avait que deux options, mauvaises toutes les deux : un champ libre où presque toute saisie est rejetée, ou un sélecteur que rien ne remplit. FederationPeer est borné à l’organisation et lisible par tout compte authentifié de celle-ci, et pas seulement par les administrateurs : un appairage est une relation de l’organisation, sans vue par sujet. L’objet porte realm (la valeur à envoyer en granteeValue), jurisdiction, state et pairedAt. Il n’expose volontairement ni la clé épinglée, ni l’ancre d’appairage, ni l’époque de clé, ni la cause de suspension : ce sont des écritures d’opérateur, et une clé publique sur chaque écran de consentement serait de la surface de sécurité achetée sans aucune fonctionnalité en échange.
FederationPeer n’a pas de méthode changes, et son state n’est pas un compteur de journal de changements : c’est une empreinte de la liste servie. L’appairage et la suspension se font dans des cérémonies d’opérateur, hors de toute requête JMAP, donc rien n’écrit de ligne de journal pour elles ; un état de journal serait ici une chaîne qui ne bouge jamais alors que les données dessous changent.

Le consentement ne se délègue pas

  • Seul le sujet peut créer ou modifier un grant, en agissant sur son propre compte. Un administrateur peut révoquer, car la révocation retire un accès et un opérateur qui doit couper une fuite n’a pas à attendre le sujet, mais jamais accorder à la place de quelqu’un. Un grant qu’un administrateur peut créer pour vous n’est pas un consentement. Le contrôle est plus strict qu’un test de rôle : une requête qui porte un accès partagé, c’est quelqu’un qui agit pour un autre, quel que soit son niveau.
  • Chaque écriture est une seule transaction : la ligne de grant, la ShareNotification qui prévient le sujet et la ligne d’audit qui nomme l’auteur de l’écriture valident ensemble. L’audit enregistre qui a réellement écrit, pas le compte que la requête visait : la révocation d’un administrateur n’est donc jamais attribuée au sujet.
  • Un granteeKind non pris en charge est refusé bruyamment. "principal" s’analyse comme une requête bien formée et aurait l’air accepté ; la ligne créée serait un grant de realm portant le nom d’une personne.
L’essentiel d’un grant est décidé par le serveur et en lecture seule sur le fil : seuls granteeKind, granteeValue et expiresAt sont réglables. rights en particulier ne l’est pas : un client capable d’élargir son propre enregistrement de consentement au-delà de ce que le résolveur honore serait une permission déguisée en réglage. expiresAt: null signifie que le sujet a explicitement choisi « sans expiration », et jurisdiction est gelée à la création : le consentement a été donné à une contrepartie dans un lieu donné, et un champ modifiable laisserait le lieu changer sans nouveau consentement.

Pourquoi un grant peut cesser d’agir, et comment le sujet l’apprend

FederationGrant.state répond directement à « pourquoi cette organisation ne me voit-elle plus ? » : active, unpaired, expired, dormancy_exhausted, dormant, lapsed ou reanchored. Le stockage le dérive du prédicat même qu’applique la lecture d’autorisation : il ne peut donc pas s’écarter de la décision. FederationPeer.state (active, dormant, lapsed) emploie les mêmes mots pour les mêmes défaillances, et seul active accepte un grant, là encore dérivé du prédicat sur lequel l’écriture refuse : un realm que le sélecteur propose est un realm que l’écriture accepte. Deux conséquences valent la peine d’être prises en compte dès la conception :
  • La révocation est unilatérale, immédiate, et ne vaut que vers l’avant. Un pair ne lit ni n’écrit jamais vos grants ; il apprend une révocation en se voyant refusé. C’est ce qui garde la révocation hors du terrain de la cohérence distribuée, et c’est aussi pourquoi revocationIsForwardOnly est annoncé comme donnée : révoquer retire le chemin de lecture pour la suite et ne défait rien de ce qui a déjà été divulgué.
  • Le journal des divulgations est l’autre moitié de cette phrase. FederationDisclosure est ce qui rend « révoquer ne défait rien de déjà divulgué » vérifiable au lieu de simplement rassurant : il survit au grant, et il n’existe aucun FederationDisclosure/set, pas même limité au destroy. C’est la rétention qui borne le journal ; le sujet n’en efface rien. Il enregistre les divulgations, jamais les tentatives : un pair qui sonde cinq cents adresses ne crée aucune ligne au sujet des quatre cent quatre-vingt-dix-neuf personnes qui n’ont jamais consenti. Chaque ligne nomme le realm du pair, la couche d’identité vérifiée, la juridiction sous laquelle le grant se tenait à cet instant, et la date.
Les filtres et les tris sur les deux méthodes query sont refusés (unsupportedFilter / unsupportedSort) plutôt qu’ignorés en silence. Un client qui filtrerait les pairs sur state: "active" et recevrait discrètement aussi les dormants proposerait à son utilisateur un bénéficiaire que l’écriture rejette ensuite.