file author tngan
*  desc import utility , { get } from './utility' ; import libsaml from './libsaml' ; import { BindingContext } from './entity' ; import { IdentityProvider as Idp } from './entity-idp' ; import { ServiceProvider as Sp } from './entity-sp' ; import * as url from 'url' ; import { wording , namespace } from './urn' ; const binding = wording . binding ; const urlParams = wording . urlParams ; export interface BuildRedirectConfig { baseUrl : string ; type : string ; isSigned : boolean ; context : string ; entitySetting : any ; relayState ? : string ; } private desc param { string } param param { string } value param { boolean } first return { string } function pvPair ( param , value , first ? ) { return ( first === true ? '?' : '&' ) + param + '=' + value ; } private desc param { string } type param { boolean } isSigned param { string } rawSamlRequest param { object } entitySetting return { string } function buildRedirectURL ( opts ) { const { baseUrl , type , isSigned , context , entitySetting , } = opts ; let { relayState = '' } = opts ; const noParams = ( url . parse ( baseUrl ) . query || [ ] ) . length === 0 ; const queryParam = libsaml . getQueryParamByType ( type ) ; const samlRequest = encodeURIComponent ( utility . base64Encode ( utility . deflateString ( context ) ) ) ; if ( relayState !== '' ) { relayState = pvPair ( urlParams . relayState , encodeURIComponent ( relayState ) ) ; } if ( isSigned ) { const sigAlg = pvPair ( urlParams . sigAlg , encodeURIComponent ( entitySetting . requestSignatureAlgorithm ) ) ; const octetString = samlRequest + relayState + sigAlg ; return baseUrl + pvPair ( queryParam , octetString , noParams ) + pvPair ( urlParams . signature , encodeURIComponent ( libsaml . constructMessageSignature ( queryParam + '=' + octetString , entitySetting . privateKey , entitySetting . privateKeyPass , undefined , entitySetting . requestSignatureAlgorithm ) . toString ( ) ) ) ; } return baseUrl + pvPair ( queryParam , samlRequest + relayState , noParams ) ; } desc param { object } entity param { function } customTagReplacement return { string } function loginRequestRedirectURL ( entity , customTagReplacement ? ) { const metadata = { idp : entity . idp . entityMeta , sp : entity . sp . entityMeta } ; const spSetting = entity . sp . entitySetting ; let id = '' ; if ( metadata && metadata . idp && metadata . sp ) { const base = metadata . idp . getSingleSignOnService ( binding . redirect ) ; let rawSamlRequest ; if ( spSetting . loginRequestTemplate && customTagReplacement ) { const info = customTagReplacement ( spSetting . loginRequestTemplate ) ; id = get ( info , 'id' , null ) ; rawSamlRequest = get ( info , 'context' , null ) ; } else { const nameIDFormat = spSetting . nameIDFormat ; const selectedNameIDFormat = Array . isArray ( nameIDFormat ) ? nameIDFormat [ 0 ] : nameIDFormat ; id = spSetting . generateID ( ) ; rawSamlRequest = libsaml . replaceTagsByValue ( libsaml . defaultLoginRequestTemplate . context , { ID : id , Destination : base , Issuer : metadata . sp . getEntityID ( ) , IssueInstant : new Date ( ) . toISOString ( ) , NameIDFormat : selectedNameIDFormat , AssertionConsumerServiceURL : metadata . sp . getAssertionConsumerService ( binding . post ) , EntityID : metadata . sp . getEntityID ( ) , AllowCreate : spSetting . allowCreate , } as any ) ; } return { id , context : buildRedirectURL ( { context : rawSamlRequest , type : urlParams . samlRequest , isSigned : metadata . sp . isAuthnRequestSigned ( ) , entitySetting : spSetting , baseUrl : base , relayState : spSetting . relayState , } ) , } ; } throw new Error ( 'ERR_GENERATE_REDIRECT_LOGIN_REQUEST_MISSING_METADATA' ) ; } desc param { object } requestInfo param { object } entity param { object } user param { String } relayState param { function } customTagReplacement function loginResponseRedirectURL ( requestInfo , entity , user = { } , relayState ? , customTagReplacement ? ) { const idpSetting = entity . idp . entitySetting ; const spSetting = entity . sp . entitySetting ; const metadata = { idp : entity . idp . entityMeta , sp : entity . sp . entityMeta , } ; let id = idpSetting . generateID ( ) ; if ( metadata && metadata . idp && metadata . sp ) { const base = metadata . sp . getAssertionConsumerService ( binding . redirect ) ; let rawSamlResponse ; const nameIDFormat = idpSetting . nameIDFormat ; const selectedNameIDFormat = Array . isArray ( nameIDFormat ) ? nameIDFormat [ 0 ] : nameIDFormat ; const nowTime = new Date ( ) ; const fiveMinutesLaterTime = new Date ( nowTime . getTime ( ) + 300000 ) ; const tvalue = { ID : id , AssertionID : idpSetting . generateID ( ) , Destination : base , SubjectRecipient : base , Issuer : metadata . idp . getEntityID ( ) , Audience : metadata . sp . getEntityID ( ) , EntityID : metadata . sp . getEntityID ( ) , IssueInstant : nowTime . toISOString ( ) , AssertionConsumerServiceURL : base , StatusCode : namespace . statusCode . success , ConditionsNotBefore : nowTime . toISOString ( ) , ConditionsNotOnOrAfter : fiveMinutesLaterTime . toISOString ( ) , SubjectConfirmationDataNotOnOrAfter : fiveMinutesLaterTime . toISOString ( ) , NameIDFormat : selectedNameIDFormat , NameID : user . email || '' , InResponseTo : get ( requestInfo , 'extract.request.id' , '' ) , AuthnStatement : '' , AttributeStatement : '' , } ; if ( idpSetting . loginResponseTemplate && customTagReplacement ) { const template = customTagReplacement ( idpSetting . loginResponseTemplate . context ) ; id = get ( template , 'id' , null ) ; rawSamlResponse = get ( template , 'context' , null ) ; } else { if ( requestInfo !== null ) { tvalue . InResponseTo = requestInfo . extract . request . id ; } rawSamlResponse = libsaml . replaceTagsByValue ( libsaml . defaultLoginResponseTemplate . context , tvalue ) ; } const { privateKey , privateKeyPass , requestSignatureAlgorithm : signatureAlgorithm } = idpSetting ; const config = { privateKey , privateKeyPass , signatureAlgorithm , signingCert : metadata . idp . getX509Certificate ( 'signing' ) , isBase64Output : false , } ; if ( metadata . sp . isWantAssertionsSigned ( ) ) { rawSamlResponse = libsaml . constructSAMLSignature ( { ... config , rawSamlMessage : rawSamlResponse , transformationAlgorithms : spSetting . transformationAlgorithms , referenceTagXPath : "/*[local-name(.)='Response']/*[local-name(.)='Assertion']" , signatureConfig : { prefix : 'ds' , location : { reference : "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']" , action : 'after' } , } , } ) ; } return { id , context : buildRedirectURL ( { baseUrl : base , type : urlParams . samlResponse , isSigned : true , context : rawSamlResponse , entitySetting : idpSetting , relayState , } ) , } ; } throw new Error ( 'ERR_GENERATE_REDIRECT_LOGIN_RESPONSE_MISSING_METADATA' ) ; } desc param { object } user param { object } entity param { function } customTagReplacement return { string } function logoutRequestRedirectURL ( user , entity , relayState ? , customTagReplacement ? ) { const metadata = { init : entity . init . entityMeta , target : entity . target . entityMeta } ; const initSetting = entity . init . entitySetting ; let id = initSetting . generateID ( ) ; const nameIDFormat = initSetting . nameIDFormat ; const selectedNameIDFormat = Array . isArray ( nameIDFormat ) ? nameIDFormat [ 0 ] : nameIDFormat ; if ( metadata && metadata . init && metadata . target ) { const base = metadata . target . getSingleLogoutService ( binding . redirect ) ; let rawSamlRequest = '' ; const requiredTags = { ID : id , Destination : base , EntityID : metadata . init . getEntityID ( ) , Issuer : metadata . init . getEntityID ( ) , IssueInstant : new Date ( ) . toISOString ( ) , NameIDFormat : selectedNameIDFormat , NameID : user . logoutNameID , SessionIndex : user . sessionIndex , } ; if ( initSetting . logoutRequestTemplate && customTagReplacement ) { const info = customTagReplacement ( initSetting . logoutRequestTemplate , requiredTags ) ; id = get ( info , 'id' , null ) ; rawSamlRequest = get ( info , 'context' , null ) ; } else { rawSamlRequest = libsaml . replaceTagsByValue ( libsaml . defaultLogoutRequestTemplate . context , requiredTags as any ) ; } return { id , context : buildRedirectURL ( { context : rawSamlRequest , relayState , type : urlParams . logoutRequest , isSigned : entity . target . entitySetting . wantLogoutRequestSigned , entitySetting : initSetting , baseUrl : base , } ) , } ; } throw new Error ( 'ERR_GENERATE_REDIRECT_LOGOUT_REQUEST_MISSING_METADATA' ) ; } desc param { object } requescorresponding param { object } entity param { function } customTagReplacement function logoutResponseRedirectURL ( requestInfo , entity , relayState ? , customTagReplacement ? ) { const metadata = { init : entity . init . entityMeta , target : entity . target . entityMeta , } ; const initSetting = entity . init . entitySetting ; let id = initSetting . generateID ( ) ; if ( metadata && metadata . init && metadata . target ) { const base = metadata . target . getSingleLogoutService ( binding . redirect ) ; let rawSamlResponse ; if ( initSetting . logoutResponseTemplate && customTagReplacement ) { const template = customTagReplacement ( initSetting . logoutResponseTemplate ) ; id = get ( template , 'id' , null ) ; rawSamlResponse = get ( template , 'context' , null ) ; } else { const tvalue = { ID : id , Destination : base , Issuer : metadata . init . getEntityID ( ) , EntityID : metadata . init . getEntityID ( ) , IssueInstant : new Date ( ) . toISOString ( ) , StatusCode : namespace . statusCode . success , } ; if ( requestInfo && requestInfo . extract && requestInfo . extract . logoutRequest ) { tvalue . InResponseTo = requestInfo . extract . logoutRequest . id ; } rawSamlResponse = libsaml . replaceTagsByValue ( libsaml . defaultLogoutResponseTemplate . context , tvalue ) ; } return { id , context : buildRedirectURL ( { baseUrl : base , type : urlParams . logoutResponse , isSigned : entity . target . entitySetting . wantLogoutResponseSigned , context : rawSamlResponse , entitySetting : initSetting , relayState , } ) , } ; } throw new Error ( 'ERR_GENERATE_REDIRECT_LOGOUT_RESPONSE_MISSING_METADATA' ) ; } const redirectBinding = { loginRequestRedirectURL , loginResponseRedirectURL , logoutRequestRedirectURL , logoutResponseRedirectURL , } ; export default redirectBinding ;