import { createLogger , ErrorWithCause , first , unique } from '@sap-cloud-sdk/util' ; import * as xsenv from '@sap/xsenv' ; import { JwtPayload } from './jsonwebtoken-type' ; import { audiences , decodeJwt } from './jwt' ; import { DestinationServiceCredentials , Service , XsuaaServiceCredentials } from './environment-accessor-types' ; const logger = createLogger ( { package : 'connectivity' , messageContext : 'environment-accessor' } ) ; returns internal export function getDestinationBasicCredentials ( ) { const destinationCredentials = getDestinationServiceCredentials ( ) ; const basicCredentials = { clientid : destinationCredentials . clientid ? destinationCredentials . clientid : null , clientsecret : destinationCredentials . clientsecret ? destinationCredentials . clientsecret : null } ; return basicCredentials ; } returns internal export function getDestinationServiceCredentials ( ) { return first ( getDestinationServiceCredentialsList ( ) ) ; } returns internal export function getDestinationServiceCredentialsList ( ) { return getServiceList ( 'destination' ) . map ( s => s . credentials as DestinationServiceCredentials ) ; } param service returns internal export function getServiceCredentialsList ( service ) { const credentials = [ ] ; getServiceList ( service ) . forEach ( entry => { if ( 'credentials' in entry ) { credentials . push ( entry [ 'credentials' ] ) ; } else { logger . warn ( `Skipping a service in ${ service }. Object has no 'credentials'.` ) ; } } ) ; return credentials ; } param service returns internal export function getServiceList ( service ) { return xsenv . filterServices ( { label : service } ) ; } param service returns internal export function getService ( service ) { const services = xsenv . filterServices ( { label : service } ) ; if ( ! services . length ) { logger . warn ( `No services of type ${ service } found! This might cause errors in other parts of the application.` ) ; return undefined ; } if ( services . length > 1 ) { logger . warn ( `Found more than one service instance for service type ${ service }. Found: ${ services . map ( s => s . name ) . join ( ', ' ) }. Selecting the first one.` ) ; } return services [ 0 ] ; } returns throws internal export function getDestinationService ( ) { const destinationService = getService ( 'destination' ) ; if ( ! destinationService ) { throw Error ( 'No binding to a destination service found.' ) ; } return destinationService ; } returns internal export function getVcapService ( ) { const env = getEnvironmentVariable ( 'VCAP_SERVICES' ) ; let vcapServices ; if ( ! env ) { logger . warn ( "Environment variable 'VCAP_SERVICES' is not defined." ) ; return null ; } try { vcapServices = JSON . parse ( env ) ; } catch ( error ) { throw new ErrorWithCause ( "Failed to parse environment variable 'VCAP_SERVICES'." , error ) ; } if ( ! Object . keys ( vcapServices ) . length ) { throw new Error ( "Environment variable 'VCAP_SERVICES' is defined but empty. This should not happen." ) ; } return vcapServices ; } param name returns internal export function getEnvironmentVariable ( name ) { if ( process . env [ name ] ) { return process . env [ name ] ; } logger . info ( 'Environment variable ' + name + ' is not defined.' ) ; return null ; } returns internal export function getDestinationServiceUri ( ) { const destinationServiceCredentials = getDestinationServiceCredentialsList ( ) ; const uris = [ ] ; for ( const credential of destinationServiceCredentials ) { if ( 'uri' in credential ) { uris . push ( credential [ 'uri' ] ) ; } else { logger . info ( "Skipping credentials in 'destination'. 'uri' property not defined" ) ; } } return uris [ 0 ] || null ; } param token returns internal export function getXsuaaServiceCredentials ( token ? ) { return typeof token === 'string' ? selectXsuaaInstance ( decodeJwt ( token ) ) : selectXsuaaInstance ( token ) ; } param service returns internal export function resolveService ( service ) { if ( typeof service === 'string' ) { const serviceInstance = getService ( service ) ; if ( ! serviceInstance ) { throw Error ( `Unable to get access token for "${ service }" service. No service instance of type "${ service }" found.` ) ; } return serviceInstance ; } return service ; } function selectXsuaaInstance ( token ? ) { const xsuaaCredentials = getServiceList ( 'xsuaa' ) . map ( service => service . credentials as XsuaaServiceCredentials ) ; if ( ! arrayHasAtLeastOneElement ( xsuaaCredentials ) ) { throw Error ( 'No binding to an XSUAA service instance found. Please make sure to bind an instance of the XSUAA service to your application.' ) ; } return token ? selectXsuaaInstanceWithJwt ( xsuaaCredentials , token ) : selectXsuaaInstanceWithoutJwt ( xsuaaCredentials ) ; } function handleOneXsuuaInstance ( xsuaaCredentials ) { logger . debug ( `Only one XSUAA instance bound. This one is used: ${ xsuaaCredentials [ 0 ] . xsappname }` ) ; return xsuaaCredentials [ 0 ] ; } function arrayHasAtLeastOneElement < T > ( array ) { return ! ! array . length && array . length > 0 ; } function arrayHasExactlyOneElement < T > ( array ) { return array . length === 1 ; } function selectXsuaaInstanceWithoutJwt ( xsuaaCredentials ) { if ( ! arrayHasExactlyOneElement ( xsuaaCredentials ) ) { logger . warn ( `The following XSUAA instances are bound: ${ xsuaaCredentials . map ( x => `\n\t- ${ x . xsappname }` ) }
      No JWT given to select XSUAA instance. ${ choseFirstOneText ( xsuaaCredentials ) }` ) ; return xsuaaCredentials [ 0 ] ; } return handleOneXsuuaInstance ( xsuaaCredentials ) ; } function selectXsuaaInstanceWithJwt ( xsuaaCredentials , jwt ) { const selectedAll = [ ... matchingClientId ( xsuaaCredentials , jwt ) , ... matchingAudience ( xsuaaCredentials , jwt ) ] ; const selectedUnique = unique ( selectedAll . map ( x => x . clientid ) ) . map ( id => selectedAll . find ( y => y . clientid === id ) ! ) ; if ( selectedUnique . length === 1 ) { logger . debug ( `One XSUAA instance found using JWT in service binding. Used service name is: ${ xsuaaCredentials [ 0 ] . xsappname }` ) ; return xsuaaCredentials [ 0 ] ; } if ( selectedUnique . length > 1 ) { logger . warn ( `Multiple XSUAA instances could be matched to the given JWT: ${ xsuaaCredentials . map ( x => `\n\t- ${ x . xsappname }` ) }
      ${ choseFirstOneText ( xsuaaCredentials ) }` ) ; return selectedUnique [ 0 ] ; } if ( ! arrayHasExactlyOneElement ( xsuaaCredentials ) ) { logger . warn ( `Multiple XSUAA instances found: ${ xsuaaCredentials . map ( x => `\n\t- ${ x . xsappname }` ) }
      None of those match either client id or audience from the given JWT. ${ choseFirstOneText ( xsuaaCredentials ) }` ) ; return xsuaaCredentials [ 0 ] ; } return handleOneXsuuaInstance ( xsuaaCredentials ) ; } function choseFirstOneText ( xsuaaCredentials ) { return `Choosing the first one (xsappname: "${ first ( xsuaaCredentials ) ! . xsappname }").` ; } function matchingClientId ( xsuaaCredentials , token ) { return xsuaaCredentials . filter ( credentials => credentials . clientid === token . client_id ) ; } function matchingAudience ( xsuaaCredentials , token ) { return xsuaaCredentials . filter ( credentials => audiences ( token ) . has ( credentials . xsappname ) ) ; } interface BasicCredentials { clientid : string ; clientsecret : string ; }