Aller au contenu

SelebeYone (SLY) — Gateway Carrefour

SelebeYone signifie carrefour en wolof. C'est le hub unique de l'écosystème DYNORS : toute requête — interne ou partenaire B2B — passe par SLY avant d'atteindre un service.

Code : SLY
Stack : Spring Cloud Gateway · WebFlux · R2DBC PostgreSQL · Redis
Repo : dynors-platform/selebeyone/


Rôle

Client (app DYNORS ou partenaire B2B)
               │
               ▼
   ┌───────────────────────┐
   │   SelebeYone (SLY)    │  ← une seule URL publique
   │  sly.dynors.com       │
   │                       │
   │  1. Identification    │  Qui appelle ? (interne ou B2B)
   │  2. Autorisation      │  Droits sur ce path ?
   │  3. Routage           │  Vers quel backend ?
   │  4. Forward + headers │  Propagation contexte
   └───────────┬───────────┘
               │  réseau privé — backends invisibles de l'extérieur
   ┌───────────▼────────────────────────────────────────┐
   │  FISCAL · BOOKS · TRACIUM · notify · DAWALALE · …  │
   └────────────────────────────────────────────────────┘

Deux types de trafic

Type Signal Validation Quotas
Interne DYNORS X-Source-App + réseau privé Token service (HMAC/JWT) + tenant en DB Non
B2B (partenaires) X-Sly-Pass Hash SHA-256 → sly_passes + scope vs path Oui

Tout autre cas → 401.


Table de routage

Path entrant Backend Variable env
/fiscal/** FISCAL FISCAL_BASE_URL
/books/** dynors-books BOOKS_BASE_URL
/tracium/** TRACIUM TRACIUM_BASE_URL
/notify/** notify NOTIFY_BASE_URL
/dawalale/** DAWALALE DAWALALE_BASE_URL
/supergest/** supergest-api SUPERGEST_BASE_URL
/mediconnect/** mediconnect-api MEDICONNECT_BASE_URL
/yobale/** yobale-api YOBALE_BASE_URL
/ragnar/** ragnar-api RAGNAR_BASE_URL
/books-app/** books-api BOOKS_APP_BASE_URL

Les routes /supergest/**, /mediconnect/**, etc. sont des routes de callback — elles permettent aux services de rappeler les applications pour les traitements asynchrones (ex. DGI → SuperGest).


Passe B2B (sly_passes)

  • Chaque partenaire B2B dispose d'un pass délivré par l'admin SLY.
  • Le pass brut n'est jamais stocké — seulement son hash SHA-256 (table sly_passes).
  • Le pass porte un tenant_code et des scopes (ex. fiscal:certify,fiscal:invoices:write).
  • Révocation : active = false dans sly_passes → effective en < 10 min (TTL cache).
  • Gestion via interface admin SLY — accès LDAP uniquement.

Scope B2B activé (v1)

Service Scopes disponibles
FISCAL fiscal:certify, fiscal:invoices:write, fiscal:invoices:read, fiscal:invoices:finalize, fiscal:invoices:pdf
TRACIUM ⏳ v2
BOOKS ⏳ v2
notify, DAWALALE ❌ interne uniquement

returnUrl (callbacks async)

Certaines opérations sont asynchrones (ex. certification DGI). L'app appelante passe un returnUrl dans le body — SLY route le callback de retour vers elle.

SuperGest → POST /fiscal/certify  (returnUrl: sly.../supergest/callbacks/fiscal)
                                         │
                              FISCAL traite + appelle DGI
                                         │
                              DGI répond → FISCAL appelle returnUrl
                                         │
                              SLY route /supergest/callbacks/... → supergest-api

Logs de routage (debug)

SLY trace chaque requête à trois niveaux :

Niveau Ce qui est loggé
INFO Requête entrante (method, path, IP, tenant), résultat final (status, durée, type, source/partner, route matchée)
DEBUG En plus : headers entrants/sortants sanitisés (valeurs sensibles masquées), route SCG matchée, URI backend résolue, signal de fin
WARN Requêtes rejetées (401/403), pass invalide, cible non résolue

Activer le mode DEBUG pour une app ou un path :

# application.yml SLY
logging:
  level:
    com.dynors.sly: DEBUG
    org.springframework.cloud.gateway: DEBUG

Exemple de log :

SLY ► IN  requestId=abc-123 POST /fiscal/api/v1/fiscal/certify ip=10.0.1.5 tenant=supergest-dakar
SLY ✓ ROUTE requestId=abc-123 POST /fiscal/api/v1/fiscal/certify → routeId=fiscal backend=http://fiscal-service:8080/api/v1/fiscal/certify
SLY ◄ END requestId=abc-123 type=INTERNAL source=supergest tenant=supergest-dakar route=fiscal backend=fiscal-service POST /fiscal/api/v1/fiscal/certify status=200 42ms signal=onComplete

Signature de transit — preuve que la requête vient de SLY

Problème résolu : sans mécanisme, rien n'empêchait une app malicieuse de forger X-Source-App: supergest et d'appeler FISCAL directement, en contournant SLY et ses contrôles.

Solution : SLY signe chaque requête qu'il transfère avec un HMAC-SHA256 calculé sur un secret partagé (SLY_FORWARD_SECRET). Les backends vérifient cette signature avant d'accepter la requête.

Algorithme

message   = "<timestamp_ms>:<X-Request-Id>:<path>"
signature = HMAC-SHA256( SLY_FORWARD_SECRET, message )

SLY ajoute deux headers :

Header Valeur Posé par Vérifié par
X-Sly-Timestamp epoch ms (ex. 1710345678123) SLY SlyTransitFilter
X-Sly-Signature hex HMAC-SHA256 SLY SlyTransitFilter

Anti-replay

Le backend rejette toute requête dont X-Sly-Timestamp est > 30 secondes dans le passé (configurable via sly-signature-window-ms).

Intégration côté backend (FISCAL, BOOKS, TRACIUM…)

@Bean
FilterRegistrationBean<SlyTransitFilter> slyTransit(
        @Value("${dynors.interapp.sly-forward-secret:}") String secret,
        @Value("${dynors.interapp.sly-signature-window-ms:30000}") long windowMs) {
    var filter = new SlyTransitFilter(secret, windowMs,
            Set.of("/actuator/health", "/v3/api-docs", "/swagger-ui"));
    var reg = new FilterRegistrationBean<>(filter);
    reg.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
    reg.addUrlPatterns("/api/*");
    return reg;
}

SlyTransitFilter et SlyTransitValidator sont dans dynors-commons (module com.dynors.commons.interapp).

Configuration complète

dynors:
  interapp:
    source-app: <code-app>
    gateway-url: ${SLY_BASE_URL:https://sly.dynors.com}
    return-base-url: ${SLY_BASE_URL:https://sly.dynors.com}/<code-app>
    cache-ttl-ms: 60000
    sly-forward-secret: ${SLY_FORWARD_SECRET}        # ← OBLIGATOIRE en production
    sly-signature-window-ms: 30000

Variable d'environnement SLY_FORWARD_SECRET : à définir comme variable CI/CD protégée et masquée dans GitLab pour chaque projet.

Développement local

En local, sly-forward-secret peut rester vide — SlyTransitFilter logue un avertissement mais laisse passer les requêtes. Ne jamais laisser vide en production.


Configurer une app pour passer par SLY

Dans application.yml de chaque app DYNORS :

dynors:
  interapp:
    source-app: <code-app>
    gateway-url: ${SLY_BASE_URL:https://sly.dynors.com}
    return-base-url: ${SLY_BASE_URL:https://sly.dynors.com}/<code-app>
    cache-ttl-ms: 60000
    sly-forward-secret: ${SLY_FORWARD_SECRET}
    sly-signature-window-ms: ${SLY_SIGNATURE_WINDOW_MS:30000}

Voir routage-interapp.md pour la synthèse complète des flux et la matrice d'appels.


Références