import { Injectable } from '@nestjs/common' ; import { AppConfig } from '@fc/app' ; import { ConfigService } from '@fc/config' ; import { OidcSession } from '@fc/oidc' ; import { ISessionBoundContext , SessionNotFoundException , SessionService , } from '@fc/session' ; import { IAppTrackingService , IEvent , IEventContext } from '@fc/tracking' ; import { getEventsMap } from '../events.map' ; import { CoreMissingContextException } from '../exceptions' ; import { ICoreTrackingContext , ICoreTrackingLog , ICoreTrackingProviders , } from '../interfaces' ; @ Injectable ( ) export class CoreTrackingService implements IAppTrackingService { readonly EventsMap ; constructor ( private readonly sessionService , private readonly config ) { this . EventsMap = getEventsMap ( this . config . get < AppConfig > ( 'App' ) . urlPrefix ) ; } async buildLog ( event , context ) { const { ip , sessionId , interactionId , claims } = await this . extractContext ( context ) ; const { step , category , event : eventName } = event ; let data ; if ( event === this . EventsMap . FC_AUTHORIZE_INITIATED ) { data = this . getDataFromContext ( context ) ; } else { data = await this . getDataFromSession ( sessionId ) ; } return { interactionId , step , category , event : eventName , ip , claims : claims ?. join ( ' ' ) , ... data , } ; } private extractIpFromContext ( context ) { if ( ! context . req . headers ) { throw new CoreMissingContextException ( 'req.headers' ) ; } const ip = context . req . headers [ 'x-forwarded-for' ] ; if ( ! ip ) { throw new CoreMissingContextException ( "req.headers['x-forwarded-for']" ) ; } return ip ; } private async extractContext ( ctx ) { /**
         * Throw rather than allow a non-loggable interaction.
         *
         * This should never happen and is a *real* exception, not a business one.
         */ const { req } = ctx ; if ( ! req ) { throw new CoreMissingContextException ( 'req' ) ; } const { claims } = req ; const ip = this . extractIpFromContext ( ctx ) ; const interactionId = this . getInteractionIdFromContext ( ctx ) ; const sessionId = req . sessionId || ( await this . sessionService . getAlias ( interactionId ) ) ; return { ip , sessionId , interactionId , claims } ; } private extractInteractionId ( req ) { return ( req . fc ?. interactionId || req . body ?. uid || req . params ?. uid || req . cookies ?. _interaction ) ; } private getInteractionIdFromContext ( { req } ) { const interactionId = this . extractInteractionId ( req ) ; if ( ! interactionId ) { throw new CoreMissingContextException ( 'interactionId missing in context' ) ; } return interactionId ; } private async getDataFromSession ( sessionId ) { const boundSessionContext = { sessionId , moduleName : 'OidcClient' , } ; const boundSessionServiceGet = this . sessionService . get . bind ( this . sessionService , boundSessionContext ) ; const sessionData = await boundSessionServiceGet ( ) ; if ( ! sessionData ) { throw new SessionNotFoundException ( boundSessionContext . moduleName ) ; } const { accountId = null , spId = null , spAcr = null , spName = null , spIdentity = null , idpId = null , idpAcr = null , idpName = null , idpIdentity = null , } = sessionData ; return { accountId , spId , spAcr , spName , TODO see https spSub : spIdentity ?. sub || null , idpId , idpAcr , idpName , idpSub : idpIdentity ?. sub || null , } ; } /**
     * Authorize logging can't rely on session since session is not created at this stage.
     * We have to do specific work to retrieve informations to log.
     */ private getDataFromContext ( context ) { if ( ! context . req ) { throw new CoreMissingContextException ( 'req' ) ; } const { spId , spAcr , spName , spSub } = context . req ; return { accountId : null , spId , spAcr , spName , spSub , idpId : null , idpAcr : null , idpName : null , idpSub : null , } ; } }