Site icon Blog Zenika

Quel identifiant exposer vers l’extérieur ?

Que ce soit au sein d’un système d’information d’entreprise (SI) ou sur le WEB, via des flux de fichiers, des API ou encore des files de messages, nos applications communiquent toujours plus avec « l’extérieur », c’est-à-dire avec d’autres programmes. Ceux-ci peuvent ne pas se baser sur les mêmes technologies, ne sont pas plus supposés connaître le fonctionnel ni les spécificités de nos applications, que nous ne connaissons les leurs.
Lorsque plusieurs applications doivent interagir de concert pour manipuler des données, la première question qui se pose a l’air toute bête : comment faire référence à ces données? Quelle information connue, même de l’application client si elle est l’instigatrice de la communication et interprétable sans ambiguïté par les deux parties, utiliser ?

Que choisir, technique ou fonctionnel ?

Le technique et le fonctionnel 

Nos applications identifient usuellement en interne leurs principaux objets métiers grâce à des identifiants : les identifiants techniques, souvent les clés primaires des tables principales et les identifiants fonctionnels.
Note : les termes “technique” et ”fonctionnel” sont certes très utilisés dans le monde de l’informatique de gestion, mais leurs définitions changent selon l’interlocuteur. On est souvent tenté de considérer que ce que l’on maîtrise mal relève forcément de l’autre « bord » … Histoire d’éviter toute ambigüité, définissons pour la suite ces termes :

Attention :

Quel type d’identifiant exposer de préférence ? 

Un identifiant technique est un identifiant à priori sans aucun sens fonctionnel (ou seulement par effet de bord, qu’il ne faudrait pas exploiter dans ce sens s’il existe un identifiant fonctionnel), et dont les principales caractéristiques sont :

Il s’agit typiquement d’une séquence, ou du détournement d’autres valeurs tenues pour uniques et non nulles par ailleurs.
Un identifiant fonctionnel est un identifiant dont l’attribution de la valeur se conforme à des règles métiers, dont les principales caractéristiques sont :

Notons que les entités que l’on stocke ne sont pas nécessairement les mêmes que celles que l’on expose (limitation à certains champs, agrégation d’autres données, traitements métiers…). Ne retenons pas aveuglément les identifiants internes pour identifier les entités que l’on exposera (à nos partenaires).
De façon générale, un identifiant technique ne devrait pas avoir à être utilisé par le fonctionnel (CI, règles métiers…), ni exposé à l’extérieur pour les raisons suivantes.

La reprise sur incident

En cas d’incident nécessitant une restauration des données (récupération d’une sauvegarde potentiellement incomplète) et le rejeu du différentiel (archive log, import de fichiers…), une même entité fonctionnelle recréée peut se voir attribuer un autre identifiant technique que celui de l’entité initialement créée. De même, la première valeur d’ID technique peut se voir affectée à une autre entité fonctionnelle.
Citons par exemple les cas suivants (liste non exhaustive) :

Si cet identifiant a déjà été propagé dans le SI ou vers l’extérieur (à un client?), cela peut provoquer de lourdes conséquences métier et d’importants coûts de chantiers correctifs. Ce point est à mon sens suffisant pour trancher ce sujet.

La complexité à propager les Id

L’identifiant technique n’ayant aucun sens hors de son application, cela ajoute une donnée supplémentaire à transmettre, que les partenaires devront connaître…
Par exemple, quand on appelle son FAI, on donne de façon générale son numéro de téléphone (identifiant  fonctionnel) plutôt qu’un identifiant obscur que l’on ne saura pas forcément où chercher à chaud.
Pour aller plus loin, si chaque application d’un SI impose aux autres ses propres numéros de séquences ou équivalent au lieu d’une information dont le fonctionnel transverse garantit déjà la diffusion et la compréhension… on n’en sort plus!

Les problématiques de typage

Même si ce n’est pas une règle absolue, les identifiants techniques sont souvent numériques ou de types natifs spécifiques à nos technologies, comme les ObjectID MongoBD par exemple. Avec les numériques, quid du respect des formats (les 0 en premiers caractères, le nombre de chiffres après la virgule, les espaces de confort…), et du dépassement de capacité d’une techno à l’autre… Quant aux types natifs spécifiques, les autres applications ont-elles vraiment à subir nos choix techniques ?
Les identifiants fonctionnels devraient quant à eux toujours être typés de façon stable (indépendamment des technologies utilisées), par exemple avec des chaînes de caractères.
Note : Afin d’éviter des difficultés prévisibles et aisément contournables, l’ensemble des caractères autorisés pour un identifiant fonctionnel devrait être restreint au jeu de caractère le plus stable possible (pour garantir l’indépendance vis-à-vis des technologies utilisées, je recommande personnellement de se restreindre à minima aux 95 caractères imprimables du jeu de caractères ASCII) et d’exclure tout caractère pouvant en gêner l’usage de l’identifiant, dans des URL notamment. Ce point est un exemple du rôle de conseil de la partie technique à la partie fonctionnelle qui épargnera à celle-ci comportements dégradés et  surcoûts.

Identifiants et sécurité

Au risque d’enfoncer des portes ouvertes, le rôle d’un identifiant, c’est d’identifier. Ne faisons pas peser sur eux les problématiques de sécurité, ce n’est pas leur job ! Dans le cas des web services, notamment des API REST, il faut bien veiller à distinguer obfuscation (rendre la bidouille d’URL complexe et espérer que cela suffise) et sécurisation.
Obfuscation : La connaissance de l’URL d’une action sur une entité m’est fournie, me permettant de l’invoquer. L’URL est supposée assez complexe pour ne pas que je puisse en déduire une autre provoquant une autre action et/ou visant une autre entité.
Cette approche est intrinsèquement déficiente pour pas mal de raisons :

Sécurisation, pour un service
Dans le cadre d’une API exposant un service, tout le monde n’est pas forcément censé pouvoir manipuler n’importe quelle ressource. De plus, à l’échelle d’une ressource donnée, tout le monde n’a pas toujours les mêmes privilèges (lecture, création, modification, suppression, actions spécifiques…). La sécurisation consiste à minima à nous assurer que l’origine de la requête est légitime dans sa demande.
A minima, l’exécution du traitement d’un service à sécuriser doit toujours être précédée des trois phases suivantes :

Par exemple, si je veux accéder à l’un de mes mails, le serveur passe par les étapes :

Note : c’est au serveur exposant une fonctionnalité d’implémenter la sécurisation des ressources qu’il expose. Ne pas reporter cette responsabilité sur l’application client.
On se moque en principe que quelqu’un ayant réellement les droits suffisant pour effectuer une action donnée sur une ressource donnée le demande par une URL manuellement bidouillée. Il nous faut par contre nous protéger des cas suivants :

Notes :

Attention, l’obfuscation (seule) permet de se rassurer, une sécurisation rigoureuse permet de se protéger 😉
Attention à la tentation de rattraper notre impuissance à contrôler les accès à nos ressources en rendant juste nos URL illisibles. La bidouille d’URL n’est qu’une menace parmi tant d’autres.

Choix du bon identifiant fonctionnel externe

On l’a dit, il nous faut un bon identifiant fonctionnel. Or, s’il est facile de décider ce qu’est un identifiant technique (c’est technique – conçu par et pour la partie technique  – et c’est à elle de le générer pour qu’il satisfasse à ses obligations), la question du bon identifiant fonctionnel à exposer à l’extérieur est épineuse, et les ratés sont loin d’être anodins.
On a instinctivement tendance à penser à la « clé » fonctionnelle de la table principale du groupe de tables, du document ou autre unité fonctionnelle stockée dans notre modèle de données, comme une référence de facture ou d’article… Attention, ce n’est pas parce qu’une propriété est « unique not null » que cela en fait un bon identifiant de ressource externe.
Le choix d’un bon identifiant fonctionnel nécessite un sérieux travail de réflexion conjoint entre

Pièges à éviter :

Note : Les contraintes liées à la confidentialité s’appliquent à toutes les informations que vous exposez (via l’URL notamment), pas seulement à l’identifiant!
Se méfier aussi des informations qu’une tierce partie pourrait déduire de ce que vous exposez (exemple : PUT nomAppli/Client/dupond_jean?religion=XXX : ce n’est pas à nous de révéler la religion de notre client, quand bien même cette catégorisation en interne serait légitime)!
Alors que choisir ?
Il n’y a pas de bonne solution universelle. La nature du métier, la cible du service et parfois l’existant influent sur ce qu’est un bon choix. Votre contexte projet pourra rendre certaines contraintes peu critiques, par exemple si vous développez un service exposant des ressources temporaires: Si leur cycle de vie ne dépasse pas quelques heures par exemple, l’instabilité à long terme des identifiants ne sera pas un drame. Par contre, ne rognez jamais sur la confidentialité, ni sur la sécurité !

Quelques réponses usuelles

Livrons-nous à une petite étude de cas.

L’identifiant naturel 

Parfois, c’est simple et il n’y a pas de piège : l’entité dispose d’un identifiant fonctionnel existant et robuste, dont les propriétés sont garanties par ailleurs,  par exemple :

Limites :

L’erreur classique à éviter est de tordre le bras à une donnée fonctionnelle pas tout à fait adaptée pour en faire artificiellement un identifiant, car ça cadre à peu près, qu’on n’a pas mieux sous la main et qu’il faut bien avancer… 

La restriction métier (ouille, là il faut être certain de son coup)

Si le problème vient de l’instabilité à la marge des identifiants dans des cas identifiés, la partie fonctionnelle peut s’engager sur des restrictions ou profiter de limitations garanties dans la durée à plus haut niveau.
Exemple : Un FAI peut utiliser les numéros de téléphone pour identifier les abonnements et tous les services, options et fournitures liées. Or un numéro de téléphone est réattribuable.
Toutefois, ils s’interdisent de réattribuer les numéros rendus inactifs avant plusieurs mois, et un numéro inactif ne peut pas être consulté par une telle API. L’ambiguïté est levée à ce prix.
Limites :  introduction de contraintes métiers ou durcissement de contraintes existantes (rendre plus complexe et coûteux encore un futur changement des règles actuelles), ici :

La composition

Une approche classique lorsqu’aucune propriété ne suffit à discriminer de façon certaine nos ressources consiste à associer plusieurs de leurs propriétés afin de lever l’ambiguïté.
Exemple : une société de location de voiture veut identifier ses locations. On suppose que les voitures sont au moins louées pour la journée. Chaque location peut donc être identifiée par la voiture et la date à laquelle la voiture a été mise à disposition. On peut alors imaginer le couple : date + numéro de châssis (Vehicle Identification Number, ou VIN).
Notez ma préférence pour le VIN, stable dans le temps, plutôt que pour la plaque d’immatriculation. Cette dernière peut changer pour un même véhicule (notamment lors de changements géographiques pour les vieilles plaques et avec les plaques temporaires) voire être réaffectée à un autre véhicule lié au même propriétaire selon la réglementation du pays l’ayant émise.  Ne jamais négliger les cas à la marge dans notre quête du meilleur identifiant.
Limites :  

L’identifiant historique

Lorsque notre seule difficulté réside dans le changement de valeur de la clé fonctionnelle dans le modèle (BDD) mais qu’un même identifiant fonctionnel n’est jamais réattribué, une solution simple est de stocker la valeur de la clé fonctionnelle (côté BDD) à la création de l’entité sans jamais plus modifier cette propriété par la suite, même lorsque la valeur de clé fonctionnelle change. Ces identifiants sont alors stables dans le temps.
Limites : 

Au final cette approche se distingue surtout de l’ID technique par la résolution des problèmes de possible réattribution des Id (techniques) suite à un incident et de typage…

L’identifiant transverse

Un client chez qui je suis intervenu se heurtait aux problèmes suivants pour identifier les ressources de son cœur de métier sur l’ensemble de son SI :

Ils ont fait une nouvelle application indexant toutes les entités métiers et ont attribué des identifiants uniques et stables. Chaque référentiel se faisait notifier un UID pour chaque nouvelle entité et devait l’intégrer à chacune des entités concernées de son modèle. Bien que de construction « technique », cet ID, normalisé et adopté par l’urbanisme, est devenu un (bon) ID fonctionnel.
Au besoin, on peut aussi mettre en place une sorte d’annuaire centralisé associant pour chaque identifiant ses métadonnées jugées utiles (application référente, identifiants internes, identifiants internes d’autres applications liées…) afin de limiter la dépendance du SI à chaque sous-référentiel et donc la charge et la criticité de ces derniers.
Limites :  

Dernier conseil pour la route…

Ce choix (de l’identifiant d’une ressource externe) – qu’il soit technique ou fonctionnel – n’a rien d’anodin et peut s’appuyer sur des hypothèses que la partie technique ne maîtrise pas toujours dans la durée. Le faire valider par le bon niveau décisionnel au sein de la partie fonctionnelle!

Dans le cas général comme dans notre cas, partie technique et partie fonctionnelle sont dans le même bateau. L’écoute est primordiale. Ne mésestimez jamais ce que l’autre partie peut apporter!

Quitter la version mobile