API Rest qui n'utilise pas le rowID dans les requêtes

Bonjour,

Pour des raisons de sécurité, nous souhaiterions ne pas utiliser le rowId de simplicité dans l’API Rest au profit d’un UUID.

Quel est la bonne pratique pour surcharger l’API standard et utiliser un autre attribut dans les opérations REST ?

Merci d’avance.

Tout d’abord comme discuté on est bien d’accord qu’on ne parle que d’appels API “GET” (= search et select unitaire) ?

Fondamentalement il y a plusieurs approches possibles:

  1. utiliser un row ID custom sur vos objets métier (et implémenter la logique pour le valoriser), le risque avec les row ID custom c’est d’avoir des comportements inattendus sur certaines features (le mécanisme row ID custom est relativement “récent” dans l’histoire de Simplicité et n’a jusqu’ici été utilisé que dans des cas assez limités fonctionnellement, ex: des objets “service”), l’avantage est par contre de bénéficier des mécanismes standards des APIs

  2. Dédier des objets métier en read-only aux APIs avec une logique de substitution du row ID dans les search/select avec un attribut métier UUID à ajouter à vos objets, on reste alors toujours sur des APIs standards, cela reste une piste à tester/explorer pour voir quels éventuels problèmes cela pourrait poser

  3. développer des APIs custom pour vos objets qui n’utiliseraient pas le row ID standard mais un attribut métier UUID à ajouter à vos objets => je vais prototyper ça pour vous fournir l’exemple le plus simple possible

Je vais revenir vers vous rapidement sur les options 2 et 3, vous pouvez expérimenter le 1 vous même en paramétrant un ou deux objets de test (ne faites pas le changement sur vos “vrais” objets métier avant d’avoir bien exploré cette piste)

A voir mais le plus simple

  • ne serait-il pas de rendre forbidden le champs row_id sur ces objets REST ?
  • et de n’ouvrir que le service “search” avec un filtre sur le champ UID spécifique si on veut faire un GET/select
  • ou alors surcharger le GET par un search filtré sur le UUID…

Mais bon fondamentalement si quelqu’un chope le token de session de quelqu’un, il n’aura aucun mal à aller chercher tous les random UUID par search sans filtre. Le pb n’est pas l’ID qu’on choisit mais bien le service ouvert.

Il faudrait préciser le cas d’usage car si c’est pour mettre juste un lien dans un email par exemple, on peut voir à créer des accès uniques par objet via un token temporaire limité à un seul record.

@Francois on est d’accord, le row ID séquentiel n’est que la partie émergée de l’iceberg, le vrai point c’est de sécuriser les accès par un filtrage “dur” (i.e. coté serveur) pour qu’un user n’ait accès que aux données auquel il a explicitement le droit => dans ce cas s’il fait /MyObject/row ID+1 ça lui renverra un 404 même si le row ID + 1 existe en base

On a discuté de cela de vive voix, leur contexte d’accès front - back est un peu complexe dans leur cas il faudra explorer plusieurs approches pour voir comment mettre en place ce filtrage “dur”, le row ID aléatoire ne sera clairement jamais une protection fiable à 100% contre des accès illégitimes, juste un “obstacle” de plus.

PS: J’ajoute effectivement l’approche 4) Mettre le row ID forbidden et n’utiliser que le search via un filtre sur un UUID métier, je ne sais pas trop comment les API génériques réagiront à ça mais ça vaut la peine de tester

Ok, dans ce cas la solution 1) est par construction celle qui doit/devra marcher en standard = si dans le code vous ne faites jamais de getField("row_id") explicite, mais bien des getRowIdField() ou autre accesseur de l’Id jamais nommé.

Si les services REST sont uniquement en lecture, c’est facile de créer des objets métier dédiés uniquement avec cet usage (avec Id mappé sur le UUID calculé au preCreate des objets back-office via UI avec rowId standard).

Ta deuxième proposition c’est mon 2)

Sur 1) j’ai peur qu’il y ait des cas pas bien gérés dans le socle pour les row ID customs, pas sur le coeur du CRUD mais plutôt sur les mécanismes plus avancés. Bref je ne pense pas que ça soit raisonnable de généraliser des row ID customs (et découvrir des cas au limites au fil de l’eau) juste pour répondre à un besoin de sécurité assez secondaire vis à vis du vrai débat sécuritaire

Voici un exemple basique de “surchouche” d’API REST (get = search/select) de l’objet produit de la demo qui se base sur l’attribut métier référence au lieu du row ID (celui-ci étant retiré des réponses)

Exemple d’usage:

select:

> curl -s -u website:simplicite https://demo.dev.simplicite.io/api/ext/DemoAPI2/REF001 | jq '.'
{
  "demoPrdType": "TABLET",
  "demoPrdSupId__demoSupCode": "PEAR",
  "demoPrdAvailable": true,
  "demoPrdSupId": "4",
  "demoPrdUnitPrice": 497,
  "demoPrdSupId__demoSupUsrId": "26",
  "demoPrdReference": "REF001",
  "demoPrdPicture": "10588",
  "demoPrdBrochure": "10589",
  "demoPrdStock": 144,
  "demoPrdName": "Smart 10' tablet",
  "demoPrdSupId__demoSupName": "Pear Inc"
}

search filtrée:

> curl -s -u website:simplicite https://dev40.dev.simplicite.io/api/ext/DemoAPI2?demoSupCode=BIM | jq '.'
[
  {
    "demoPrdType": "LAPTOP",
    "demoPrdSupId__demoSupCode": "BIM",
    "demoPrdAvailable": true,
    "demoPrdSupId": "1",
    "demoPrdUnitPrice": 850,
    "demoPrdSupId__demoSupUsrId": "10",
    "demoPrdReference": "REF003",
    "demoPrdPicture": "26991",
    "demoPrdBrochure": "26992",
    "demoPrdStock": 761,
    "demoPrdName": "Infinite laptop",
    "demoPrdSupId__demoSupName": "BIM Computers Ldt"
  },
  {
    "demoPrdType": "OTHER",
    "demoPrdSupId__demoSupCode": "BIM",
    "demoPrdAvailable": true,
    "demoPrdSupId": "1",
    "demoPrdUnitPrice": 149,
    "demoPrdSupId__demoSupUsrId": "10",
    "demoPrdReference": "REF005",
    "demoPrdPicture": "26993",
    "demoPrdBrochure": "26994",
    "demoPrdStock": 220,
    "demoPrdName": "Wide curved screen",
    "demoPrdSupId__demoSupName": "BIM Computers Ldt"
  }
]

J’ai rendu cet exemple générique en utilisant un param système qui donne la liste des objets utilisables dans l’URI et le nom de l’attribut identifiant à utiliser pour cet objet:

Le module est à jour sur GitHub: module-demo-apis/DemoAPI2.java at master · simplicitesoftware/module-demo-apis · GitHub

Ex d’usages:

get product:

> curl -s -u website:simplicite https://demo.dev.simplicite.io/api/ext/DemoAPI2/DemoProduct/REF001 | jq '.'
{
  "demoPrdType": "TABLET",
  "demoPrdSupId__demoSupCode": "PEAR",
  "demoPrdAvailable": true,
  "demoPrdSupId": "4",
  "demoPrdUnitPrice": 497,
  "demoPrdSupId__demoSupUsrId": "11",
  "demoPrdReference": "REF001",
  "demoPrdPicture": "27003",
  "demoPrdBrochure": "27004",
  "demoPrdStock": 144,
  "demoPrdName": "Smart 10' tablet",
  "demoPrdSupId__demoSupName": "Pear Inc"
}

get supplier:

> curl -s -u website:simplicite https://demo.dev.simplicite.io/api/ext/DemoAPI2/DemoSupplier/BIM | jq '.'
{
  "demoSupCode": "BIM",
  "demoSupName": "BIM Computers Ldt",
  "demoSupLogo": "26982",
  "demoSupUsrId": "10",
  "demoSupWebsite": "http://www.simplicite.fr"
}

etc.

1 Like

Merci pour cette réponse bien illustrée.