Façade d'API et Sécurité

Ce document présente les points d'entrée de RestFrontage pour authentifier un client, consulter ou modifier la sécurité, filtrer les objets OData et utiliser les outils Razor.

Pour le modèle conceptuel et les règles de décision, consulter Présentation de la sécurité dans Immersive.

Authentification d'un client API

Obtenir un jeton

POST {{baseUrl}}/api/Security/User/Authenticate
Content-Type: application/json

{
  "AccountName": "utilisateur",
  "Password": "mot-de-passe"
}

Alias accepté : POST /api/Security/Users/Authenticate.

Réponse simplifiée :

{
  "Domain": "DC=Immersive, DC=GraphicStream, DC=fr",
  "Token": "eyJ...",
  "AuthenticatedUser": {
    "Identifier": 123,
    "DistinguishedNameValue": "AN=utilisateur, OU=Users, ..."
  }
}

Le client transmet ensuite :

Authorization: Bearer {{token}}

Les identifiants et le mot de passe doivent être envoyés uniquement en HTTPS.

Cycle du jeton

Verbe et route Fonction
POST /api/Security/Token/Renew Renouveler un jeton valide
GET ou POST /api/Security/Token/Validate Vérifier que le pipeline accepte le jeton
GET ou POST /api/Security/Whoami Obtenir le nom de l'identité courante

Endpoints principaux de sécurité

Toutes les routes ci-dessous ont le préfixe /api/Security.

Contrôle et administration des droits

Verbe et route Usage État recommandé
GET /Users/{askerId}/Rights/{flags}/SecurizedObjects/{type}/{objectId} Tester un masque sur un objet Historique ; n'intègre pas les partages
GET /Rights?securedObjectIdentifier=...&securedObjectTypeIdentifier=... Lire les droits explicites d'un objet Utilisable pour l'administration
POST /Rights Appliquer une collection de droits via ISecurityAdministrationService Point d'écriture recommandé
DELETE /Rights?rightIdentifiers=... Supprimer des droits par identifiant Historique ; à migrer vers la façade centrale
GET /Permissions Lire les permissions configurées Recommandé pour construire une interface sans flags codés en dur

Exemple d'application d'un droit :

POST {{baseUrl}}/api/Security/Rights
Authorization: Bearer {{token}}
Content-Type: application/json

[
  {
    "Identifier": 0,
    "DomainSecurityObjectIdentifier": 145,
    "SecurizedObjectIdentifier": 902,
    "SecurizedObjectType": 15,
    "AuthorizedFlags": 3,
    "DeniedFlags": 0
  }
]

Règles de POST /Rights :

  • l'acteur est toujours lu depuis le contexte authentifié ;
  • Cerebrate et les administrateurs racine peuvent administrer tous les droits ;
  • sinon l'acteur et les bénéficiaires doivent appartenir à l'organisation courante ;
  • un bénéficiaire BuiltIn est protégé ;
  • l'acteur doit pouvoir administrer chaque objet cible ;
  • les flags doivent correspondre aux permissions configurées ;
  • AuthorizedFlags = 0 et DeniedFlags = 0 supprime le droit correspondant ;
  • plusieurs requêtes visant le même triplet sont normalisées avant l'écriture.

Réponses d'erreur :

  • 400 : données ou flags invalides ;
  • 401 : absence d'identité authentifiée ;
  • 403 : acteur non autorisé ou mauvaise organisation ;
  • 409 : incohérence ou conflit de données, par exemple anciens droits dupliqués.

Partages

Verbe et route Usage
POST /SharedAccess Créer un partage dont AccessLinkIdentifier référence une TinyUrl existante
POST /SharedAccess/Aggregate Créer explicitement un agrégat et préciser la TinyUrl au niveau racine
POST /SharedAccess/{grantIdentifier}/Revoke Révoquer le partage sans supprimer la TinyUrl

Exemple de partage interne pendant une semaine :

POST {{baseUrl}}/api/Security/SharedAccess/Aggregate
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "TinyUrlIdentifier": "equipment-902-share",
  "Grant": {
    "BeneficiaryDomainSecurityObjectIdentifier": 245,
    "ExternalRecipientEmail": null,
    "ValidFromUtc": "2026-08-18T10:00:00Z",
    "ExpiresAtUtc": "2026-08-25T10:00:00Z",
    "Rights": [
      {
        "SecurizedObjectIdentifier": 902,
        "SecurizedObjectType": 18,
        "AuthorizedFlags": 1
      },
      {
        "SecurizedObjectIdentifier": 84,
        "SecurizedObjectType": 15,
        "AuthorizedFlags": 1
      },
      {
        "SecurizedObjectIdentifier": 31,
        "SecurizedObjectType": 48,
        "AuthorizedFlags": 1
      }
    ]
  }
}

Pour un destinataire externe, renseigner ExternalRecipientEmail et laisser BeneficiaryDomainSecurityObjectIdentifier à null. Exactement un des deux champs doit être défini.

Réponse simplifiée :

{
  "grantIdentifier": 12,
  "publicIdentifier": "...",
  "externalAccessToken": "retourné uniquement pour un partage externe",
  "tinyUrlIdentifier": "equipment-902-share",
  "tinyUrlPath": "/?TU=equipment-902-share",
  "externalAccessPath": "/Shared?token=...&tinyUrl=equipment-902-share"
}

Le serveur ne déduit aucune dépendance. Beholder, Forge ou le Player doit envoyer la liste complète des objets nécessaires à l'ouverture de la cible.

Il n'existe pas encore de route REST dédiée à la liste des partages. Cette liste est actuellement consommée par la partial Razor via GetSharedAccessGrantsAsync.

Appel minimal de la modale de partage :

/Security/Modals/SharedAccess
    ?SecurizedObjectIdentifier={identifier}
    &SecurizedObjectType={type}
    &TinyUrlIdentifier={tinyUrlExistante}

Annuaire

La façade expose aussi les familles suivantes :

Famille Routes principales
Résolution GET /DomainObjects/{dnOuId}, POST /Discover
Navigation GET /DomainObjects/{dn}/Members, GET /Explorer/{dn}/Search/{criteria}
Appartenances GET, POST, PUT, DELETE /DomainObjects/{dn}/Membership
Membres de groupe PUT, POST, DELETE /Groups/{dn}/Members
Hiérarchie GET /DomainObjects/{dn}/GroupMembershipHierarchy
Champs extensibles GET, PUT, POST /DomainObjects/{dn}/Fields
Utilisateurs POST /Users, PUT /Users, PATCH /Users({id}), DELETE /Users/{id}
Groupes POST /Groups, PUT /Groups, PATCH /Groups({id}), DELETE /Groups/{id}
OU POST /OrganizationalUnits, PUT /OrganizationalUnits, PATCH /OrganizationalUnits({id}), DELETE /OrganizationalUnits/{id}
Reconstruction d'organisation POST /OrganizationalUnits/{id}/SubDomain/Rebuild

Ces routes d'annuaire sont en partie historiques et ne passent pas toutes par ISecurityAdministrationService. Toute nouvelle mutation doit utiliser la façade transversale ou étendre celle-ci.

OData sécurisé

ODataCrudController<TEntity> applique la sécurité seulement si le type Immersive possède des permissions configurées.

Pour un type sécurisé :

  • GET filtre le IQueryable avant l'application des options OData ;
  • GET(key) et les navigations vérifient la visibilité du parent ;
  • PUT et PATCH exigent la permission de modification du contrôleur ;
  • DELETE exige la permission de suppression du contrôleur ;
  • POST exige Cerebrate, un administrateur racine ou le groupe Administrators du sous-domaine cible ;
  • après création, les droits automatiques sont appliqués par la façade d'administration.
Entity set Type Lecture Modification Suppression
/odata/Maps 15 ReadMap ModifyMap FullControlMap
/odata/Equipments 18 ReadEquipment ModifyEquipment FullControlEquipment
/odata/Workspaces 37 ReadWorkspace ModifyWorkspace FullControlWorkspace
/odata/SupervisionScopes 48 ReadSupervisionScope ModifySupervisionScope FullControlSupervisionScope

Exemple :

GET {{baseUrl}}/odata/SupervisionScopes?$filter=Name eq 'Yvetot'&$select=Identifier,Name
Authorization: Bearer {{token}}
Accept: application/json

Le filtre de sécurité et le filtre OData sont composés puis exécutés par la base métier. Le contrôleur ne doit pas télécharger toute la collection avant de filtrer.

Pages utiles du RestFrontage

Entrées principales

Chemin Fonction Accès
/Security Tableau de bord du domaine courant Utilisateur authentifié
/Security/Explorer Parcourir les OU, utilisateurs, groupes et devices Utilisateur authentifié
/Security/UsersAndGroups Rechercher et administrer utilisateurs et groupes Utilisateur authentifié ; mutations contrôlées par leurs handlers
/Security/RightsTester Expliquer un droit effectif Utilisateur authentifié et organisation courante
/Security/Tools Audits et réparations de sécurité Cerebrate uniquement

Testeur de droits

Le testeur permet de choisir :

  1. un utilisateur ou un groupe ;
  2. un type d'objet reconnu ;
  3. un objet appartenant à l'organisation courante ;
  4. une permission configurée pour ce type.

Il produit un AuditAccess qui détaille les chemins d'appartenance ayant autorisé ou interdit l'accès. Le catalogue détecte les types à partir des DbSet<TEntity> dont l'entité implémente IIdentifiedObject et possède un ImmersiveObjectAttribute.

Limites actuelles : sélection plafonnée à 5 000 objets par type et absence des partages internes dans le rapport.

Outils Cerebrate

La page /Security/Tools est accessible uniquement par Cerebrate. Elle ne doit pas servir d'interface d'administration courante.

Unicité des droits

L'outil affiche :

  • le nombre de droits ;
  • les groupes dupliqués ;
  • la présence de l'index unique SQL.

Le nettoyage manuel actuel conserve la ligne la plus récente du groupe choisi. La migration 20260815_EnforceUniqueRights suit une stratégie plus conservatrice : elle fusionne d'abord tous les flags autorisés et interdits avant de supprimer les anciennes lignes.

Structure des organisations

L'audit vérifie pour chaque organisation :

  • les OU Groups, Users et Devices ;
  • les groupes Administrators, Users et Sharers ;
  • l'utilisateur {Organisation}_Admin ;
  • les appartenances standard ;
  • les groupes conventionnels de permission déjà reconnus.

La réparation appelle EnsureSubDomainDefaultsAsync et préserve les données existantes.

Droits automatiques

L'audit compare les droits attendus et enregistrés pour :

  • les cartes ;
  • les équipements ;
  • les workspaces ;
  • les scopes de supervision.

Il peut réparer un objet ou une sélection. La réparation ajoute seulement les flags automatiques manquants.

Points de vigilance et durcissements à prévoir

Priorité haute

  1. GET /api/Security/ResetDomain porte actuellement [AllowAnonymous]. Même s'il ne modifie pas les droits, il provoque un rechargement et une invalidation globale ; il doit être protégé ou déplacé vers la console de récupération.
  2. DELETE /api/Security/Rights supprime directement dans SecurityModel sans passer par l'autorisation de ISecurityAdministrationService. Il doit être remplacé par POST /Rights avec des flags à zéro ou par une méthode centrale dédiée.
  3. Les partages internes ne sont pas encore consultés par ObjectSecurityService. Ils sont persistés mais ne donnent pas encore accès dans OData.
  4. Le test historique HaveRight et le testeur Razor utilisent SecurityUtils directement et ne voient pas les partages.

Priorité de cohérence

  1. Centraliser progressivement les mutations d'utilisateurs, groupes, OU et appartenances.
  2. Ajouter une route authentifiée de sélection d'organisation adaptée aux clients Bearer stateless, ou inclure un contexte d'organisation contrôlé dans le contrat API.
  3. Exposer une route de liste des partages si les clients WebGL doivent administrer les partages sans partial Razor.
  4. Étendre l'audit des droits automatiques lorsque de nouveaux types sont branchés au pipeline.
  5. Retirer à terme les alias historiques après migration des clients.

Ajouter un nouveau type sécurisé

Pour étendre la façade sans créer un service par type :

  1. déclarer le type dans ImmersiveObjectType ;
  2. appliquer ImmersiveObjectAttribute sur l'entité ;
  3. enregistrer ses Permission dans le domaine et l'instantané ObjectsToPermissions ;
  4. dériver le contrôleur d'ODataCrudController<TEntity> ;
  5. fournir ReadPermission, UpdatePermission et DeletePermission ;
  6. injecter les services transversaux existants ;
  7. garantir que ISubDomainService peut retrouver l'organisation de l'entité ;
  8. ajouter son RightsDefaultPattern si nécessaire ;
  9. ajouter le type à l'audit des droits automatiques ;
  10. tester lecture, création, modification, suppression, interdiction, Cerebrate et multi-instance.

Fichiers de référence

| Sujet | Fichier | |---|---| | Contrôleur REST | Controllers/SecurityController.cs | | Contrôleur de session/organisation | Controllers/SessionController.cs | | Pipeline JWT | Commons/Immersive.RestFrontage.Components/Security/TokenParserMiddleware.cs, TokenCheckMiddleware.cs | | Contrôleur OData générique | corporate/Commons/GraphicStream.Server.Components/Controllers/ODataCrudController.cs | | Catalogue du testeur | Security/SecurizedObjectCatalog.cs | | Testeur | Pages/Security/RightsTester.cshtml.cs | | Outils | Pages/Security/Tools | | Modale de partage | Pages/Security/Modals/SharedAccess.cshtml et .cshtml.cs | | Page externe | Pages/Shared/Index.cshtml et .cshtml.cs |