@rockcarver/frodo-lib 0.16.2-8 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +221 -1
- package/cjs/api/AgentApi.js +14 -61
- package/cjs/api/AgentApi.js.map +1 -1
- package/cjs/api/AgentApi.test.js.map +1 -1
- package/cjs/api/ApiTypes.js +0 -5
- package/cjs/api/ApiTypes.js.map +1 -1
- package/cjs/api/AuthenticateApi.js +9 -20
- package/cjs/api/AuthenticateApi.js.map +1 -1
- package/cjs/api/AuthenticateApi.test.js.map +1 -0
- package/cjs/api/BaseApi.js +82 -124
- package/cjs/api/BaseApi.js.map +1 -1
- package/cjs/api/CirclesOfTrustApi.js +8 -32
- package/cjs/api/CirclesOfTrustApi.js.map +1 -1
- package/cjs/api/IdmConfigApi.js +9 -34
- package/cjs/api/IdmConfigApi.js.map +1 -1
- package/cjs/api/LogApi.js +8 -31
- package/cjs/api/LogApi.js.map +1 -1
- package/cjs/api/ManagedObjectApi.js +34 -31
- package/cjs/api/ManagedObjectApi.js.map +1 -1
- package/cjs/api/NodeApi.js +10 -41
- package/cjs/api/NodeApi.js.map +1 -1
- package/cjs/api/NodeApi.test.js.map +1 -1
- package/cjs/api/OAuth2ClientApi.js +7 -26
- package/cjs/api/OAuth2ClientApi.js.map +1 -1
- package/cjs/api/OAuth2OIDCApi.js +28 -34
- package/cjs/api/OAuth2OIDCApi.js.map +1 -1
- package/cjs/api/OAuth2OIDCApi.test.js.map +1 -0
- package/cjs/api/OAuth2ProviderApi.js +5 -15
- package/cjs/api/OAuth2ProviderApi.js.map +1 -1
- package/cjs/api/RealmApi.js +8 -34
- package/cjs/api/RealmApi.js.map +1 -1
- package/cjs/api/Saml2Api.js +32 -118
- package/cjs/api/Saml2Api.js.map +1 -1
- package/cjs/api/ScriptApi.js +8 -30
- package/cjs/api/ScriptApi.js.map +1 -1
- package/cjs/api/SecretsApi.js +14 -64
- package/cjs/api/SecretsApi.js.map +1 -1
- package/cjs/api/SecretsApi.test.js.map +1 -1
- package/cjs/api/ServerInfoApi.js +6 -21
- package/cjs/api/ServerInfoApi.js.map +1 -1
- package/cjs/api/ServiceApi.js +49 -47
- package/cjs/api/ServiceApi.js.map +1 -1
- package/cjs/api/SocialIdentityProvidersApi.js +9 -36
- package/cjs/api/SocialIdentityProvidersApi.js.map +1 -1
- package/cjs/api/StartupApi.js +5 -24
- package/cjs/api/StartupApi.js.map +1 -1
- package/cjs/api/StartupApi.test.js.map +1 -1
- package/cjs/api/TreeApi.js +8 -30
- package/cjs/api/TreeApi.js.map +1 -1
- package/cjs/api/TreeApi.test.js.map +1 -1
- package/cjs/api/VariablesApi.js +9 -37
- package/cjs/api/VariablesApi.js.map +1 -1
- package/cjs/api/VariablesApi.test.js.map +1 -1
- package/cjs/api/utils/ApiUtils.js +47 -57
- package/cjs/api/utils/ApiUtils.js.map +1 -1
- package/cjs/api/utils/ApiUtils.test.js.map +1 -1
- package/cjs/api/utils/Base64.js +6 -15
- package/cjs/api/utils/Base64.js.map +1 -1
- package/cjs/ext/axios-curlirize/curlirize.js +2 -9
- package/cjs/ext/axios-curlirize/curlirize.js.map +1 -1
- package/cjs/ext/axios-curlirize/lib/CurlHelper.js +7 -22
- package/cjs/ext/axios-curlirize/lib/CurlHelper.js.map +1 -1
- package/cjs/index.js +30 -89
- package/cjs/index.js.map +1 -1
- package/cjs/ops/AdminOps.js +27 -165
- package/cjs/ops/AdminOps.js.map +1 -1
- package/cjs/ops/AgentOps.js +3 -160
- package/cjs/ops/AgentOps.js.map +1 -1
- package/cjs/ops/AgentOps.test.js.map +1 -1
- package/cjs/ops/AuthenticateOps.js +265 -226
- package/cjs/ops/AuthenticateOps.js.map +1 -1
- package/cjs/ops/AuthenticateOps.test.js.map +1 -1
- package/cjs/ops/CirclesOfTrustOps.js +13 -91
- package/cjs/ops/CirclesOfTrustOps.js.map +1 -1
- package/cjs/ops/ConnectionProfileOps.js +220 -136
- package/cjs/ops/ConnectionProfileOps.js.map +1 -1
- package/cjs/ops/ConnectionProfileOps.test.js.map +1 -1
- package/cjs/ops/EmailTemplateOps.js +35 -379
- package/cjs/ops/EmailTemplateOps.js.map +1 -1
- package/cjs/ops/EmailTemplateOps.test.js.map +1 -1
- package/cjs/ops/IdmOps.js +52 -426
- package/cjs/ops/IdmOps.js.map +1 -1
- package/cjs/ops/IdmOps.test.js.map +1 -1
- package/cjs/ops/IdpOps.js +11 -94
- package/cjs/ops/IdpOps.js.map +1 -1
- package/cjs/ops/IdpOps.test.js.map +1 -1
- package/cjs/ops/JoseOps.js +81 -0
- package/cjs/ops/JoseOps.js.map +1 -0
- package/cjs/ops/JoseOps.test.js.map +1 -0
- package/cjs/ops/JourneyOps.js +140 -371
- package/cjs/ops/JourneyOps.js.map +1 -1
- package/cjs/ops/JourneyOps.test.js.map +1 -1
- package/cjs/ops/LogOps.js +14 -47
- package/cjs/ops/LogOps.js.map +1 -1
- package/cjs/ops/ManagedObjectOps.js +4 -12
- package/cjs/ops/ManagedObjectOps.js.map +1 -1
- package/cjs/ops/NodeOps.js +9 -67
- package/cjs/ops/NodeOps.js.map +1 -1
- package/cjs/ops/OAuth2ClientOps.js +10 -40
- package/cjs/ops/OAuth2ClientOps.js.map +1 -1
- package/cjs/ops/OpsTypes.js +0 -5
- package/cjs/ops/OpsTypes.js.map +1 -1
- package/cjs/ops/OrganizationOps.js +6 -25
- package/cjs/ops/OrganizationOps.js.map +1 -1
- package/cjs/ops/RealmOps.js +0 -19
- package/cjs/ops/RealmOps.js.map +1 -1
- package/cjs/ops/Saml2Ops.js +258 -802
- package/cjs/ops/Saml2Ops.js.map +1 -1
- package/cjs/ops/Saml2Ops.test.js.map +1 -1
- package/cjs/ops/ScriptOps.js +28 -73
- package/cjs/ops/ScriptOps.js.map +1 -1
- package/cjs/ops/SecretsOps.js +2 -58
- package/cjs/ops/SecretsOps.js.map +1 -1
- package/cjs/ops/ServiceAccountOps.js +61 -0
- package/cjs/ops/ServiceAccountOps.js.map +1 -0
- package/cjs/ops/ServiceAccountOps.test.js.map +1 -0
- package/cjs/ops/ServiceOps.js +55 -101
- package/cjs/ops/ServiceOps.js.map +1 -1
- package/cjs/ops/StartupOps.js +2 -23
- package/cjs/ops/StartupOps.js.map +1 -1
- package/cjs/ops/ThemeOps.js +230 -436
- package/cjs/ops/ThemeOps.js.map +1 -1
- package/cjs/ops/ThemeOps.test.js.map +1 -0
- package/cjs/ops/VariablesOps.js +0 -38
- package/cjs/ops/VariablesOps.js.map +1 -1
- package/cjs/ops/utils/Console.js +29 -62
- package/cjs/ops/utils/Console.js.map +1 -1
- package/cjs/ops/utils/DataProtection.js +13 -47
- package/cjs/ops/utils/DataProtection.js.map +1 -1
- package/cjs/ops/utils/DataProtection.test.js.map +1 -1
- package/cjs/ops/utils/ExportImportUtils.js +36 -74
- package/cjs/ops/utils/ExportImportUtils.js.map +1 -1
- package/cjs/ops/utils/ExportImportUtils.test.js.map +1 -1
- package/cjs/ops/utils/OpsUtils.js +27 -43
- package/cjs/ops/utils/OpsUtils.js.map +1 -1
- package/cjs/ops/utils/OpsUtils.test.js.map +1 -1
- package/cjs/ops/utils/ValidationUtils.js +0 -13
- package/cjs/ops/utils/ValidationUtils.js.map +1 -1
- package/cjs/ops/utils/ValidationUtils.test.js.map +1 -1
- package/cjs/ops/utils/Version.js +2 -12
- package/cjs/ops/utils/Version.js.map +1 -1
- package/cjs/ops/utils/Version.test.js.map +1 -1
- package/cjs/ops/utils/Wordwrap.js +1 -2
- package/cjs/ops/utils/Wordwrap.js.map +1 -1
- package/cjs/ops/utils/Wordwrap.test.js.map +1 -0
- package/cjs/shared/State.js +239 -0
- package/cjs/shared/State.js.map +1 -0
- package/cjs/shared/State.test.js.map +1 -0
- package/cjs/storage/StaticStorage.js.map +1 -1
- package/cjs/test/mocks/AuthenticateApi/step/default_steps.json +88 -0
- package/cjs/test/mocks/ForgeRockApiMockEngine.js +184 -80
- package/cjs/test/mocks/ForgeRockApiMockEngine.js.map +1 -1
- package/cjs/test/mocks/IdmConfigApi/getConfigEntity/managed.json +4420 -0
- package/cjs/test/mocks/OAuth2OIDCApi/accessToken/body.json +6 -0
- package/cjs/test/mocks/OAuth2OIDCApi/accessToken/headers.json +19 -0
- package/cjs/test/mocks/OAuth2OIDCApi/authorize/headers.json +38 -0
- package/cjs/test/mocks/Saml2Api/findSaml2Providers/https:sts.windows.net711ffa9c-5972-4713-ace3-688c9732614a_stub.json +18 -0
- package/cjs/test/mocks/Saml2Api/findSaml2Providers/iSPAzure_stub.json +18 -0
- package/cjs/test/mocks/Saml2Api/getProvider/aVNQQXp1cmU.json +1 -2
- package/cjs/test/mocks/Saml2Api/getProvider/dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.json +1 -3
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/U1BBenVyZQ.json +147 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQQXp1cmU.json +182 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQQnJvYWRjb20.json +149 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQRmVkbGV0.json +141 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aVNQQXp1cmU.json +1 -2
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/dm9sa2VyRGV2U1A.json +118 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aHR0cHM6Ly9zYW1sLm15dGVzdHJ1bi5jb20vc3A.json +66 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzExZmZhOWMtNTk3Mi00NzEzLWFjZTMtNjg4Yzk3MzI2MTRhLw.json +35 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aWRw.json +83 -0
- package/cjs/test/mocks/Saml2Api/getProviderByLocationAndId/remote/dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.json +1 -3
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/SPAzure.xml +74 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPAzure.xml +78 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPBroadcom.xml +78 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPFedlet.xml +78 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fsaml.mytestrun.com%2Fsp.xml +13 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/{dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.xml → https%3A%2F%2Fsts.windows.net%2F711ffa9c-5972-4713-ace3-688c9732614a%2F.xml} +0 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/{aVNQQXp1cmU.xml → iSPAzure.xml} +0 -0
- package/cjs/test/mocks/Saml2Api/getProviderMetadata/alpha/urn%3Afederation%3AMicrosoftOnline.xml +84 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/SPAzure.saml.json +243 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/iSPAzure.saml.json +242 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPAzure.saml.json +454 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPBroadcom.saml.json +586 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPFedlet.saml.json +241 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/idp.saml.json +185 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/saml.mytestrun.comsp.saml.json +101 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/sts.windows.net711ffa9c-5972-4713-ace3-688c9732614a.saml.json +113 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/urn:federation:MicrosoftOnline.saml.json +199 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Provider/volkerDevSP.saml.json +212 -0
- package/cjs/test/mocks/Saml2Ops/importSaml2Providers/allAlphaProviders.saml.json +2414 -0
- package/cjs/test/mocks/ScriptApi/getScript/5b29c5b7-b161-4a42-a41f-d6c85316b951.json +13 -0
- package/cjs/test/mocks/ScriptApi/getScript/85523e71-2d77-4577-b078-6f9674cc54e2.json +13 -0
- package/cjs/test/mocks/ScriptApi/getScript/90c4eca5-05f0-42f5-b9bf-88b693eabbbd.json +13 -0
- package/cjs/test/mocks/ServerInfoApi/getServerInfo/info.json +25 -0
- package/cjs/test/mocks/ServerInfoApi/getServerVersionInfo/version.json +8 -0
- package/cjs/utils/AutoSetupPolly.js +79 -0
- package/cjs/utils/AutoSetupPolly.js.map +1 -0
- package/cjs/utils/SetupJest.js +6 -0
- package/cjs/utils/SetupJest.js.map +1 -0
- package/cjs/{test/mocks → utils}/snapshotResolve.js +10 -12
- package/cjs/utils/snapshotResolve.js.map +1 -0
- package/esm/api/AgentApi.mjs +19 -21
- package/esm/api/AgentApi.test.mjs +6 -15
- package/esm/api/ApiTypes.mjs +0 -5
- package/esm/api/AuthenticateApi.mjs +7 -10
- package/esm/api/AuthenticateApi.test.mjs +39 -0
- package/esm/api/BaseApi.mjs +96 -81
- package/esm/api/CirclesOfTrustApi.mjs +9 -12
- package/esm/api/IdmConfigApi.mjs +11 -12
- package/esm/api/LogApi.mjs +6 -10
- package/esm/api/ManagedObjectApi.mjs +23 -8
- package/esm/api/NodeApi.mjs +13 -15
- package/esm/api/NodeApi.test.mjs +13 -17
- package/esm/api/OAuth2ClientApi.mjs +7 -9
- package/esm/api/OAuth2OIDCApi.mjs +15 -9
- package/esm/api/OAuth2OIDCApi.test.mjs +53 -0
- package/esm/api/OAuth2ProviderApi.mjs +3 -5
- package/esm/api/RealmApi.mjs +10 -13
- package/esm/api/Saml2Api.mjs +31 -61
- package/esm/api/ScriptApi.mjs +9 -11
- package/esm/api/SecretsApi.mjs +21 -24
- package/esm/api/SecretsApi.test.mjs +9 -31
- package/esm/api/ServerInfoApi.mjs +5 -9
- package/esm/api/ServiceApi.mjs +53 -26
- package/esm/api/SocialIdentityProvidersApi.mjs +11 -13
- package/esm/api/StartupApi.mjs +5 -10
- package/esm/api/StartupApi.test.mjs +4 -7
- package/esm/api/TreeApi.mjs +9 -11
- package/esm/api/TreeApi.test.mjs +24 -61
- package/esm/api/VariablesApi.mjs +11 -13
- package/esm/api/VariablesApi.test.mjs +80 -222
- package/esm/api/utils/ApiUtils.mjs +45 -48
- package/esm/api/utils/ApiUtils.test.mjs +33 -32
- package/esm/api/utils/Base64.mjs +6 -9
- package/esm/ext/axios-curlirize/curlirize.mjs +2 -7
- package/esm/ext/axios-curlirize/lib/CurlHelper.mjs +7 -20
- package/esm/index.mjs +23 -13
- package/esm/ops/AdminOps.mjs +33 -119
- package/esm/ops/AgentOps.mjs +38 -81
- package/esm/ops/AgentOps.test.mjs +5 -45
- package/esm/ops/AuthenticateOps.mjs +241 -175
- package/esm/ops/AuthenticateOps.test.mjs +7 -9
- package/esm/ops/CirclesOfTrustOps.mjs +24 -61
- package/esm/ops/ConnectionProfileOps.mjs +192 -82
- package/esm/ops/ConnectionProfileOps.test.mjs +19 -19
- package/esm/ops/EmailTemplateOps.mjs +18 -276
- package/esm/ops/EmailTemplateOps.test.mjs +19 -44
- package/esm/ops/IdmOps.mjs +30 -327
- package/esm/ops/IdmOps.test.mjs +47 -54
- package/esm/ops/IdpOps.mjs +21 -59
- package/esm/ops/IdpOps.test.mjs +5 -5
- package/esm/ops/JoseOps.mjs +41 -0
- package/esm/ops/JoseOps.test.mjs +137 -0
- package/esm/ops/JourneyOps.mjs +147 -290
- package/esm/ops/JourneyOps.test.mjs +30 -28
- package/esm/ops/LogOps.mjs +15 -25
- package/esm/ops/ManagedObjectOps.mjs +6 -6
- package/esm/ops/NodeOps.mjs +9 -47
- package/esm/ops/OAuth2ClientOps.mjs +13 -19
- package/esm/ops/OpsTypes.mjs +1 -3
- package/esm/ops/OrganizationOps.mjs +7 -14
- package/esm/ops/RealmOps.mjs +4 -7
- package/esm/ops/Saml2Ops.mjs +199 -587
- package/esm/ops/Saml2Ops.test.mjs +303 -84
- package/esm/ops/ScriptOps.mjs +31 -47
- package/esm/ops/SecretsOps.mjs +15 -31
- package/esm/ops/ServiceAccountOps.mjs +41 -0
- package/esm/ops/ServiceAccountOps.test.mjs +51 -0
- package/esm/ops/ServiceOps.mjs +66 -83
- package/esm/ops/StartupOps.mjs +4 -13
- package/esm/ops/ThemeOps.mjs +189 -298
- package/esm/{api/ThemeApi.test.mjs → ops/ThemeOps.test.mjs} +91 -96
- package/esm/ops/VariablesOps.mjs +9 -18
- package/esm/ops/utils/Console.mjs +28 -46
- package/esm/ops/utils/DataProtection.mjs +15 -28
- package/esm/ops/utils/DataProtection.test.mjs +8 -8
- package/esm/ops/utils/ExportImportUtils.mjs +34 -43
- package/esm/ops/utils/ExportImportUtils.test.mjs +20 -18
- package/esm/ops/utils/OpsUtils.mjs +27 -21
- package/esm/ops/utils/OpsUtils.test.mjs +155 -27
- package/esm/ops/utils/ValidationUtils.mjs +0 -9
- package/esm/ops/utils/Version.mjs +2 -3
- package/esm/ops/utils/Version.test.mjs +0 -2
- package/esm/ops/utils/Wordwrap.mjs +1 -1
- package/esm/ops/utils/Wordwrap.test.mjs +19 -0
- package/esm/shared/State.mjs +164 -0
- package/esm/shared/State.test.mjs +249 -0
- package/esm/test/mocks/AuthenticateApi/step/default_steps.json +88 -0
- package/esm/test/mocks/ForgeRockApiMockEngine.mjs +169 -33
- package/esm/test/mocks/IdmConfigApi/getConfigEntity/managed.json +4420 -0
- package/esm/test/mocks/OAuth2OIDCApi/accessToken/body.json +6 -0
- package/esm/test/mocks/OAuth2OIDCApi/accessToken/headers.json +19 -0
- package/esm/test/mocks/OAuth2OIDCApi/authorize/headers.json +38 -0
- package/esm/test/mocks/Saml2Api/findSaml2Providers/https:sts.windows.net711ffa9c-5972-4713-ace3-688c9732614a_stub.json +18 -0
- package/esm/test/mocks/Saml2Api/findSaml2Providers/iSPAzure_stub.json +18 -0
- package/esm/test/mocks/Saml2Api/getProvider/aVNQQXp1cmU.json +1 -2
- package/esm/test/mocks/Saml2Api/getProvider/dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.json +1 -3
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/U1BBenVyZQ.json +147 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQQXp1cmU.json +182 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQQnJvYWRjb20.json +149 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aHR0cHM6Ly9pZGMuc2NoZXViZXIuaW8vYW0vc2FtbDIvSURQRmVkbGV0.json +141 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/aVNQQXp1cmU.json +1 -2
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/hosted/dm9sa2VyRGV2U1A.json +118 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aHR0cHM6Ly9zYW1sLm15dGVzdHJ1bi5jb20vc3A.json +66 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzExZmZhOWMtNTk3Mi00NzEzLWFjZTMtNjg4Yzk3MzI2MTRhLw.json +35 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/remote/aWRw.json +83 -0
- package/esm/test/mocks/Saml2Api/getProviderByLocationAndId/remote/dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.json +1 -3
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/SPAzure.xml +74 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPAzure.xml +78 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPBroadcom.xml +78 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fidc.scheuber.io%2Fam%2Fsaml2%2FIDPFedlet.xml +78 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/https%3A%2F%2Fsaml.mytestrun.com%2Fsp.xml +13 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/{dXJuOmZlZGVyYXRpb246TWljcm9zb2Z0T25saW5l.xml → https%3A%2F%2Fsts.windows.net%2F711ffa9c-5972-4713-ace3-688c9732614a%2F.xml} +0 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/{aVNQQXp1cmU.xml → iSPAzure.xml} +0 -0
- package/esm/test/mocks/Saml2Api/getProviderMetadata/alpha/urn%3Afederation%3AMicrosoftOnline.xml +84 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/SPAzure.saml.json +243 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/iSPAzure.saml.json +242 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPAzure.saml.json +454 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPBroadcom.saml.json +586 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/idc.scheuber.ioamsaml2IDPFedlet.saml.json +241 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/idp.saml.json +185 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/saml.mytestrun.comsp.saml.json +101 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/sts.windows.net711ffa9c-5972-4713-ace3-688c9732614a.saml.json +113 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/urn:federation:MicrosoftOnline.saml.json +199 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Provider/volkerDevSP.saml.json +212 -0
- package/esm/test/mocks/Saml2Ops/importSaml2Providers/allAlphaProviders.saml.json +2414 -0
- package/esm/test/mocks/ScriptApi/getScript/5b29c5b7-b161-4a42-a41f-d6c85316b951.json +13 -0
- package/esm/test/mocks/ScriptApi/getScript/85523e71-2d77-4577-b078-6f9674cc54e2.json +13 -0
- package/esm/test/mocks/ScriptApi/getScript/90c4eca5-05f0-42f5-b9bf-88b693eabbbd.json +13 -0
- package/esm/test/mocks/ServerInfoApi/getServerInfo/info.json +25 -0
- package/esm/test/mocks/ServerInfoApi/getServerVersionInfo/version.json +8 -0
- package/esm/utils/AutoSetupPolly.mjs +72 -0
- package/esm/utils/SetupJest.mjs +3 -0
- package/esm/{test/mocks → utils}/snapshotResolve.mjs +10 -12
- package/package.json +38 -27
- package/types/api/AgentApi.d.ts.map +1 -1
- package/types/api/ApiTypes.d.ts +19 -15
- package/types/api/ApiTypes.d.ts.map +1 -1
- package/types/api/AuthenticateApi.d.ts +1 -1
- package/types/api/AuthenticateApi.d.ts.map +1 -1
- package/types/api/BaseApi.d.ts +2 -0
- package/types/api/BaseApi.d.ts.map +1 -1
- package/types/api/CirclesOfTrustApi.d.ts.map +1 -1
- package/types/api/IdmConfigApi.d.ts.map +1 -1
- package/types/api/LogApi.d.ts.map +1 -1
- package/types/api/ManagedObjectApi.d.ts +9 -1
- package/types/api/ManagedObjectApi.d.ts.map +1 -1
- package/types/api/NodeApi.d.ts.map +1 -1
- package/types/api/OAuth2ClientApi.d.ts.map +1 -1
- package/types/api/OAuth2OIDCApi.d.ts +4 -2
- package/types/api/OAuth2OIDCApi.d.ts.map +1 -1
- package/types/api/OAuth2ProviderApi.d.ts.map +1 -1
- package/types/api/RealmApi.d.ts.map +1 -1
- package/types/api/Saml2Api.d.ts +7 -13
- package/types/api/Saml2Api.d.ts.map +1 -1
- package/types/api/ScriptApi.d.ts.map +1 -1
- package/types/api/SecretsApi.d.ts.map +1 -1
- package/types/api/ServerInfoApi.d.ts.map +1 -1
- package/types/api/ServiceApi.d.ts +14 -7
- package/types/api/ServiceApi.d.ts.map +1 -1
- package/types/api/SocialIdentityProvidersApi.d.ts.map +1 -1
- package/types/api/StartupApi.d.ts.map +1 -1
- package/types/api/TreeApi.d.ts.map +1 -1
- package/types/api/VariablesApi.d.ts.map +1 -1
- package/types/api/utils/ApiUtils.d.ts +1 -0
- package/types/api/utils/ApiUtils.d.ts.map +1 -1
- package/types/api/utils/Base64.d.ts +1 -0
- package/types/api/utils/Base64.d.ts.map +1 -1
- package/types/index.d.ts +11 -5
- package/types/index.d.ts.map +1 -1
- package/types/ops/AgentOps.d.ts +1 -1
- package/types/ops/AgentOps.d.ts.map +1 -1
- package/types/ops/AuthenticateOps.d.ts +10 -2
- package/types/ops/AuthenticateOps.d.ts.map +1 -1
- package/types/ops/ConnectionProfileOps.d.ts +49 -21
- package/types/ops/ConnectionProfileOps.d.ts.map +1 -1
- package/types/ops/EmailTemplateOps.d.ts +14 -33
- package/types/ops/EmailTemplateOps.d.ts.map +1 -1
- package/types/ops/IdmOps.d.ts +29 -37
- package/types/ops/IdmOps.d.ts.map +1 -1
- package/types/ops/JoseOps.d.ts +33 -0
- package/types/ops/JoseOps.d.ts.map +1 -0
- package/types/ops/JourneyOps.d.ts.map +1 -1
- package/types/ops/LogOps.d.ts.map +1 -1
- package/types/ops/NodeOps.d.ts.map +1 -1
- package/types/ops/OAuth2ClientOps.d.ts.map +1 -1
- package/types/ops/OpsTypes.d.ts +17 -1
- package/types/ops/OpsTypes.d.ts.map +1 -1
- package/types/ops/OrganizationOps.d.ts.map +1 -1
- package/types/ops/Saml2Ops.d.ts +52 -55
- package/types/ops/Saml2Ops.d.ts.map +1 -1
- package/types/ops/ScriptOps.d.ts.map +1 -1
- package/types/ops/ServiceAccountOps.d.ts +20 -0
- package/types/ops/ServiceAccountOps.d.ts.map +1 -0
- package/types/ops/ServiceOps.d.ts +18 -10
- package/types/ops/ServiceOps.d.ts.map +1 -1
- package/types/ops/ThemeOps.d.ts +35 -50
- package/types/ops/ThemeOps.d.ts.map +1 -1
- package/types/ops/utils/Console.d.ts +1 -1
- package/types/ops/utils/Console.d.ts.map +1 -1
- package/types/ops/utils/DataProtection.d.ts.map +1 -1
- package/types/ops/utils/ExportImportUtils.d.ts +5 -2
- package/types/ops/utils/ExportImportUtils.d.ts.map +1 -1
- package/types/ops/utils/OpsUtils.d.ts +6 -0
- package/types/ops/utils/OpsUtils.d.ts.map +1 -1
- package/types/ops/utils/Wordwrap.d.ts.map +1 -1
- package/types/shared/State.d.ts +223 -0
- package/types/shared/State.d.ts.map +1 -0
- package/types/test/mocks/ForgeRockApiMockEngine.d.ts +21 -1
- package/types/test/mocks/ForgeRockApiMockEngine.d.ts.map +1 -1
- package/types/utils/AutoSetupPolly.d.ts +1 -0
- package/types/utils/AutoSetupPolly.d.ts.map +1 -0
- package/types/utils/SetupJest.d.ts +1 -0
- package/types/utils/SetupJest.d.ts.map +1 -0
- package/types/utils/snapshotResolve.d.ts +1 -0
- package/types/utils/snapshotResolve.d.ts.map +1 -0
- package/cjs/api/EmailTemplateApi.js +0 -73
- package/cjs/api/EmailTemplateApi.js.map +0 -1
- package/cjs/api/ThemeApi.js +0 -367
- package/cjs/api/ThemeApi.js.map +0 -1
- package/cjs/api/ThemeApi.test.js.map +0 -1
- package/cjs/storage/SessionStorage.js +0 -91
- package/cjs/storage/SessionStorage.js.map +0 -1
- package/cjs/test/mocks/snapshotResolve.js.map +0 -1
- package/esm/api/EmailTemplateApi.mjs +0 -34
- package/esm/api/ThemeApi.mjs +0 -271
- package/esm/storage/SessionStorage.mjs +0 -79
- package/types/api/EmailTemplateApi.d.ts +0 -22
- package/types/api/EmailTemplateApi.d.ts.map +0 -1
- package/types/api/ThemeApi.d.ts +0 -56
- package/types/api/ThemeApi.d.ts.map +0 -1
- package/types/storage/SessionStorage.d.ts +0 -71
- package/types/storage/SessionStorage.d.ts.map +0 -1
- package/types/test/mocks/snapshotResolve.d.ts +0 -1
- package/types/test/mocks/snapshotResolve.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/JourneyOps.ts"],"names":[],"mappings":"AAwDA,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAiB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAgBpB;;;GAGG;AACH,wBAAgB,8BAA8B,IAAI,yBAAyB,CAa1E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,wBAAwB,CAKxE;AAsFD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,iBAGR,GACA,OAAO,CAAC,yBAAyB,CAAC,CAqXpC;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAU3D;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,yBAAyB,EACrC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAgef;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,gBAAgB,KAAA,EAChB,UAAU,KAAA,EACV,kBAAkB,KAAA,EAClB,gBAAgB,KAAA,EAChB,KAAK,SAAK,iBA4DX;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,iBAAiB,iBAuC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,gBAAgB,EAAE,yBAAyB,GAC1C,wBAAwB,GAAG,6BAA6B,CAe1D;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,2BAOpC,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,2BAyBtC,CAAC;AAEJ;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,GACX,2BAA2B,CA2B7B;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,yBAAyB,EACrC,iBAAiB,GAAE,2BAAsD,EACzE,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CAiCrC;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAgF5D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,OAAO,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAepB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,yBAAyB,WAUjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,WAUlE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,WAUpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,yBAAyB,GACjC,qBAAqB,EAAE,CAczB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,KAAA,EACP,QAAQ,UAAO;;GAqKhB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,KAAA,eAmC3C;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWxE","file":"JourneyOps.d.ts","sourcesContent":["import fs from 'fs';\nimport { v4 as uuidv4 } from 'uuid';\nimport _ from 'lodash';\nimport {\n convertBase64TextToArray,\n getTypedFilename,\n convertTextArrayToBase64,\n convertTextArrayToBase64Url,\n findFilesByName,\n} from './utils/ExportImportUtils';\nimport { getRealmManagedUser, replaceAll } from './utils/OpsUtils';\nimport storage from '../storage/SessionStorage';\nimport {\n getNode,\n putNode,\n deleteNode,\n getNodeTypes,\n getNodesByType,\n} from '../api/NodeApi';\nimport { isCloudOnlyNode, isCustomNode, isPremiumNode } from './NodeOps';\nimport { getTrees, getTree, putTree, deleteTree } from '../api/TreeApi';\nimport { getEmailTemplate, putEmailTemplate } from '../api/EmailTemplateApi';\nimport { getScript } from '../api/ScriptApi';\nimport * as global from '../storage/StaticStorage';\nimport {\n printMessage,\n createProgressIndicator,\n updateProgressIndicator,\n stopProgressIndicator,\n debugMessage,\n} from './utils/Console';\nimport {\n getProviderByLocationAndId,\n getProviders,\n getProviderMetadata,\n createProvider,\n findProviders,\n updateProvider,\n} from '../api/Saml2Api';\nimport {\n createCircleOfTrust,\n getCirclesOfTrust,\n updateCircleOfTrust,\n} from '../api/CirclesOfTrustApi';\nimport {\n decode,\n encode,\n encodeBase64Url,\n isBase64Encoded,\n} from '../api/utils/Base64';\nimport {\n getSocialIdentityProviders,\n putProviderByTypeAndId,\n} from '../api/SocialIdentityProvidersApi';\nimport { getThemes, putThemes } from '../api/ThemeApi';\nimport { createOrUpdateScript } from './ScriptOps';\nimport { JourneyClassification, TreeExportResolverInterface } from './OpsTypes';\nimport { ThemeSkeleton, TreeSkeleton } from '../api/ApiTypes';\nimport {\n InnerNodeRefSkeletonInterface,\n NodeRefSkeletonInterface,\n NodeSkeleton,\n} from '../api/ApiTypes';\nimport {\n SingleTreeExportInterface,\n MultiTreeExportInterface,\n TreeDependencyMapInterface,\n TreeExportOptions,\n TreeImportOptions,\n} from './OpsTypes';\n\nconst containerNodes = ['PageNode', 'CustomPageNode'];\n\nconst scriptedNodes = [\n 'ConfigProviderNode',\n 'ScriptedDecisionNode',\n 'ClientScriptNode',\n 'SocialProviderHandlerNode',\n 'CustomScriptNode',\n];\n\nconst emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];\n\nconst emptyScriptPlaceholder = '[Empty]';\n\n/**\n * Create an empty single tree export template\n * @returns {SingleTreeExportInterface} an empty single tree export template\n */\nexport function createSingleTreeExportTemplate(): SingleTreeExportInterface {\n return {\n meta: {},\n innerNodes: {},\n nodes: {},\n scripts: {},\n emailTemplates: {},\n socialIdentityProviders: {},\n themes: [],\n saml2Entities: {},\n circlesOfTrust: {},\n tree: {},\n } as SingleTreeExportInterface;\n}\n\n/**\n * Create an empty multi tree export template\n * @returns {MultiTreeExportInterface} an empty multi tree export template\n */\nexport function createMultiTreeExportTemplate(): MultiTreeExportInterface {\n return {\n meta: {},\n trees: {},\n } as MultiTreeExportInterface;\n}\n\n/**\n * Helper to get all SAML2 dependencies for a given node object\n * @param {Object} nodeObject node object\n * @param {[Object]} allProviders array of all saml2 providers objects\n * @param {[Object]} allCirclesOfTrust array of all circle of trust objects\n * @returns {Promise} a promise that resolves to an object containing a saml2 dependencies\n */\nasync function getSaml2NodeDependencies(\n nodeObject,\n allProviders,\n allCirclesOfTrust\n) {\n const samlProperties = ['metaAlias', 'idpEntityId'];\n const saml2EntityPromises = [];\n for (const samlProperty of samlProperties) {\n // In the following line nodeObject[samlProperty] will look like '/alpha/iSPAzure'.\n const entityId =\n samlProperty === 'metaAlias'\n ? _.last(nodeObject[samlProperty].split('/'))\n : nodeObject[samlProperty];\n const entity = _.find(allProviders, { entityId });\n if (entity) {\n try {\n const providerResponse = await getProviderByLocationAndId(\n entity.location,\n entity._id\n );\n /**\n * Adding entityLocation here to the entityResponse because the import tool\n * needs to know whether the saml2 entity is remote or not (this will be removed\n * from the config before importing see updateSaml2Entity and createSaml2Entity functions).\n * Importing a remote saml2 entity is a slightly different request (see createSaml2Entity).\n */\n providerResponse.entityLocation = entity.location;\n\n if (entity.location === 'remote') {\n // get the xml representation of this entity and add it to the entityResponse;\n const metaDataResponse = await getProviderMetadata(\n providerResponse.entityId\n );\n providerResponse.base64EntityXML = encodeBase64Url(metaDataResponse);\n }\n saml2EntityPromises.push(providerResponse);\n } catch (error) {\n printMessage(error.message, 'error');\n }\n }\n }\n try {\n const saml2EntitiesPromisesResults = await Promise.all(saml2EntityPromises);\n const saml2Entities = [];\n for (const saml2Entity of saml2EntitiesPromisesResults) {\n if (saml2Entity) {\n saml2Entities.push(saml2Entity);\n }\n }\n const samlEntityIds = _.map(\n saml2Entities,\n (saml2EntityConfig) => `${saml2EntityConfig.entityId}|saml2`\n );\n const circlesOfTrust = _.filter(allCirclesOfTrust, (circleOfTrust) => {\n let hasEntityId = false;\n for (const trustedProvider of circleOfTrust.trustedProviders) {\n if (!hasEntityId && samlEntityIds.includes(trustedProvider)) {\n hasEntityId = true;\n }\n }\n return hasEntityId;\n });\n const saml2NodeDependencies = {\n saml2Entities,\n circlesOfTrust,\n };\n return saml2NodeDependencies;\n } catch (error) {\n printMessage(error.message, 'error');\n const saml2NodeDependencies = {\n saml2Entities: [],\n circlesOfTrust: [],\n };\n return saml2NodeDependencies;\n }\n}\n\n/**\n * Create export data for a tree/journey with all its nodes and dependencies. The export data can be written to a file as is.\n * @param {string} treeId tree id/name\n * @param {TreeExportOptions} options export options\n * @returns {Promise<SingleTreeExportInterface>} a promise that resolves to an object containing the tree and all its nodes and dependencies\n */\nexport async function exportJourney(\n treeId: string,\n options: TreeExportOptions = {\n useStringArrays: true,\n deps: true,\n }\n): Promise<SingleTreeExportInterface> {\n const exportData = createSingleTreeExportTemplate();\n try {\n const treeObject = await getTree(treeId);\n const { useStringArrays, deps } = options;\n const verbose = storage.session.getDebug();\n\n if (verbose) printMessage(`\\n- ${treeObject._id}\\n`, 'info', false);\n\n // Process tree\n if (verbose) printMessage(' - Flow');\n exportData.tree = treeObject;\n if (verbose && treeObject.identityResource)\n printMessage(\n ` - identityResource: ${treeObject.identityResource}`,\n 'info'\n );\n if (verbose) printMessage(` - Done`, 'info');\n\n const nodePromises = [];\n const scriptPromises = [];\n const emailTemplatePromises = [];\n const innerNodePromises = [];\n const saml2ConfigPromises = [];\n let socialProviderPromise = null;\n let themePromise = null;\n if (\n deps &&\n storage.session.getDeploymentType() !== global.CLASSIC_DEPLOYMENT_TYPE_KEY\n ) {\n try {\n themePromise = getThemes();\n } catch (error) {\n printMessage(error, 'error');\n }\n }\n\n let allSaml2Providers = null;\n let allCirclesOfTrust = null;\n let filteredSocialProviders = null;\n const themes = [];\n\n // get all the nodes\n for (const [nodeId, nodeInfo] of Object.entries(treeObject.nodes)) {\n nodePromises.push(getNode(nodeId, nodeInfo['nodeType']));\n }\n if (verbose && nodePromises.length > 0) printMessage(' - Nodes:');\n const nodeObjects = await Promise.all(nodePromises);\n\n // iterate over every node in tree\n for (const nodeObject of nodeObjects) {\n const nodeId = nodeObject._id;\n const nodeType = nodeObject._type._id;\n if (verbose) printMessage(` - ${nodeId} (${nodeType})`, 'info', true);\n exportData.nodes[nodeObject._id] = nodeObject;\n\n // handle script node types\n if (\n deps &&\n scriptedNodes.includes(nodeType) &&\n nodeObject.script !== emptyScriptPlaceholder\n ) {\n scriptPromises.push(getScript(nodeObject.script));\n }\n\n // frodo supports email templates in platform deployments\n if (\n (deps &&\n storage.session.getDeploymentType() ===\n global.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n storage.session.getDeploymentType() ===\n global.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n if (emailTemplateNodes.includes(nodeType)) {\n try {\n const emailTemplate = await getEmailTemplate(\n nodeObject.emailTemplateName\n );\n emailTemplatePromises.push(emailTemplate);\n } catch (error) {\n let message = `${error}`;\n if (error.isAxiosError && error.response.status) {\n message = error.response.statusText;\n }\n printMessage(\n `\\n${message}: Email Template \"${nodeObject.emailTemplateName}\"`,\n 'error'\n );\n }\n }\n }\n\n // handle SAML2 node dependencies\n if (deps && nodeType === 'product-Saml2Node') {\n if (!allSaml2Providers) {\n // eslint-disable-next-line no-await-in-loop\n allSaml2Providers = (await getProviders()).result;\n }\n if (!allCirclesOfTrust) {\n // eslint-disable-next-line no-await-in-loop\n allCirclesOfTrust = (await getCirclesOfTrust()).result;\n }\n saml2ConfigPromises.push(\n getSaml2NodeDependencies(\n nodeObject,\n allSaml2Providers,\n allCirclesOfTrust\n )\n );\n }\n\n // If this is a SocialProviderHandlerNode get each enabled social identity provider.\n if (\n deps &&\n !socialProviderPromise &&\n nodeType === 'SocialProviderHandlerNode'\n ) {\n socialProviderPromise = getSocialIdentityProviders();\n }\n\n // If this is a SelectIdPNode and filteredProviters is not already set to empty array set filteredSocialProviers.\n if (deps && !filteredSocialProviders && nodeType === 'SelectIdPNode') {\n filteredSocialProviders = filteredSocialProviders || [];\n for (const filteredProvider of nodeObject.filteredProviders) {\n if (!filteredSocialProviders.includes(filteredProvider)) {\n filteredSocialProviders.push(filteredProvider);\n }\n }\n }\n\n // get inner nodes (nodes inside container nodes)\n if (containerNodes.includes(nodeType)) {\n for (const innerNode of nodeObject.nodes) {\n innerNodePromises.push(getNode(innerNode._id, innerNode.nodeType));\n }\n // frodo supports themes in platform deployments\n if (\n (deps &&\n storage.session.getDeploymentType() ===\n global.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n storage.session.getDeploymentType() ===\n global.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n let themeId = false;\n\n if (nodeObject.stage) {\n // see if themeId is part of the stage object\n try {\n themeId = JSON.parse(nodeObject.stage).themeId;\n } catch (e) {\n themeId = false;\n }\n // if the page node's themeId is set the \"old way\" set themeId accordingly\n if (!themeId && nodeObject.stage.indexOf('themeId=') === 0) {\n // eslint-disable-next-line prefer-destructuring\n themeId = nodeObject.stage.split('=')[1];\n }\n }\n\n if (themeId) {\n if (!themes.includes(themeId)) themes.push(themeId);\n }\n }\n }\n }\n\n // Process inner nodes\n if (verbose && innerNodePromises.length > 0)\n printMessage(' - Inner nodes:');\n const innerNodeDataResults = await Promise.all(innerNodePromises);\n for (const innerNodeObject of innerNodeDataResults) {\n const innerNodeId = innerNodeObject._id;\n const innerNodeType = innerNodeObject._type._id;\n if (verbose)\n printMessage(` - ${innerNodeId} (${innerNodeType})`, 'info', true);\n exportData.innerNodes[innerNodeId] = innerNodeObject;\n\n // handle script node types\n if (deps && scriptedNodes.includes(innerNodeType)) {\n scriptPromises.push(getScript(innerNodeObject.script));\n }\n\n // frodo supports email templates in platform deployments\n if (\n (deps &&\n storage.session.getDeploymentType() ===\n global.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n storage.session.getDeploymentType() ===\n global.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n if (emailTemplateNodes.includes(innerNodeType)) {\n try {\n const emailTemplate = await getEmailTemplate(\n innerNodeObject.emailTemplateName\n );\n emailTemplatePromises.push(emailTemplate);\n } catch (error) {\n let message = `${error}`;\n if (error.isAxiosError && error.response.status) {\n message = error.response.statusText;\n }\n printMessage(\n `\\n${message}: Email Template \"${innerNodeObject.emailTemplateName}\"`,\n 'error'\n );\n }\n }\n }\n\n // handle SAML2 node dependencies\n if (deps && innerNodeType === 'product-Saml2Node') {\n printMessage('SAML2 inner node', 'error');\n if (!allSaml2Providers) {\n // eslint-disable-next-line no-await-in-loop\n allSaml2Providers = (await getProviders()).result;\n }\n if (!allCirclesOfTrust) {\n // eslint-disable-next-line no-await-in-loop\n allCirclesOfTrust = (await getCirclesOfTrust()).result;\n }\n saml2ConfigPromises.push(\n getSaml2NodeDependencies(\n innerNodeObject,\n allSaml2Providers,\n allCirclesOfTrust\n )\n );\n }\n\n // If this is a SocialProviderHandlerNode get each enabled social identity provider.\n if (\n deps &&\n !socialProviderPromise &&\n innerNodeType === 'SocialProviderHandlerNode'\n ) {\n socialProviderPromise = getSocialIdentityProviders();\n }\n\n // If this is a SelectIdPNode and filteredProviters is not already set to empty array set filteredSocialProviers.\n if (\n deps &&\n !filteredSocialProviders &&\n innerNodeType === 'SelectIdPNode' &&\n innerNodeObject.filteredProviders\n ) {\n filteredSocialProviders = filteredSocialProviders || [];\n for (const filteredProvider of innerNodeObject.filteredProviders) {\n if (!filteredSocialProviders.includes(filteredProvider)) {\n filteredSocialProviders.push(filteredProvider);\n }\n }\n }\n }\n\n // Process email templates\n if (verbose && emailTemplatePromises.length > 0)\n printMessage(' - Email templates:');\n const settledEmailTemplatePromises = await Promise.allSettled(\n emailTemplatePromises\n );\n for (const settledPromise of settledEmailTemplatePromises) {\n if (settledPromise.status === 'fulfilled' && settledPromise.value) {\n if (verbose)\n printMessage(\n ` - ${settledPromise.value._id.split('/')[1]}${\n settledPromise.value.displayName\n ? ` (${settledPromise.value.displayName})`\n : ''\n }`,\n 'info',\n true\n );\n exportData.emailTemplates[settledPromise.value._id.split('/')[1]] =\n settledPromise.value;\n }\n }\n\n // Process SAML2 providers and circles of trust\n const saml2NodeDependencies = await Promise.all(saml2ConfigPromises);\n for (const saml2NodeDependency of saml2NodeDependencies) {\n if (saml2NodeDependency) {\n if (verbose) printMessage(' - SAML2 entity providers:');\n for (const saml2Entity of saml2NodeDependency.saml2Entities) {\n if (verbose)\n printMessage(\n ` - ${saml2Entity.entityLocation} ${saml2Entity.entityId}`,\n 'info'\n );\n exportData.saml2Entities[saml2Entity._id] = saml2Entity;\n }\n if (verbose) printMessage(' - SAML2 circles of trust:');\n for (const circleOfTrust of saml2NodeDependency.circlesOfTrust) {\n if (verbose) printMessage(` - ${circleOfTrust._id}`, 'info');\n exportData.circlesOfTrust[circleOfTrust._id] = circleOfTrust;\n }\n }\n }\n\n // Process socialIdentityProviders\n const socialProviders = await Promise.resolve(socialProviderPromise);\n if (socialProviders) {\n if (verbose) printMessage(' - OAuth2/OIDC (social) identity providers:');\n for (const socialProvider of socialProviders.result) {\n // If the list of socialIdentityProviders needs to be filtered based on the\n // filteredProviders property of a SelectIdPNode do it here.\n if (\n socialProvider &&\n (!filteredSocialProviders ||\n filteredSocialProviders.length === 0 ||\n filteredSocialProviders.includes(socialProvider._id))\n ) {\n if (verbose) printMessage(` - ${socialProvider._id}`, 'info');\n scriptPromises.push(getScript(socialProvider.transform));\n exportData.socialIdentityProviders[socialProvider._id] =\n socialProvider;\n }\n }\n }\n\n // Process scripts\n if (verbose && scriptPromises.length > 0) printMessage(' - Scripts:');\n const scriptObjects = await Promise.all(scriptPromises);\n for (const scriptObject of scriptObjects) {\n if (scriptObject) {\n if (verbose)\n printMessage(\n ` - ${scriptObject._id} (${scriptObject.name})`,\n 'info',\n true\n );\n scriptObject.script = useStringArrays\n ? convertBase64TextToArray(scriptObject.script)\n : JSON.stringify(decode(scriptObject.script));\n exportData.scripts[scriptObject._id] = scriptObject;\n }\n }\n\n // Process themes\n if (themePromise) {\n if (verbose) printMessage(' - Themes:');\n try {\n const themePromiseResults = await Promise.resolve(themePromise);\n for (const themeObject of themePromiseResults) {\n if (\n themeObject &&\n // has the theme been specified by id or name in a page node?\n (themes.includes(themeObject._id) ||\n themes.includes(themeObject.name) ||\n // has this journey been linked to a theme?\n themeObject.linkedTrees?.includes(treeObject._id))\n ) {\n if (verbose)\n printMessage(\n ` - ${themeObject._id} (${themeObject.name})`,\n 'info'\n );\n exportData.themes.push(themeObject);\n }\n }\n } catch (error) {\n printMessage(error.response.data, 'error');\n printMessage('Error handling themes: ' + error.message, 'error');\n }\n }\n } catch (error) {\n printMessage(error.response.data, 'error');\n printMessage(\n 'Error exporting tree: ' + treeId + ' - ' + error.message,\n 'error'\n );\n }\n\n return exportData;\n}\n\n/**\n * Get all the journeys/trees without all their nodes and dependencies.\n * @returns {Promise<TreeSkeleton[]>} a promise that resolves to an array of journey objects\n */\nexport async function getJourneys(): Promise<TreeSkeleton[]> {\n let journeys = [];\n try {\n journeys = (await getTrees()).result;\n } catch (error) {\n printMessage(`${error.message}`, 'error');\n printMessage(error.response.data, 'error');\n }\n journeys.sort((a, b) => a._id.localeCompare(b._id));\n return journeys;\n}\n\n/**\n * Helper to import a tree with all dependencies from a `SingleTreeExportInterface` object (typically read from a file)\n * @param {SingleTreeExportInterface} treeObject tree object containing tree and all its dependencies\n * @param {TreeImportOptions} options import options\n */\nexport async function importJourney(\n treeObject: SingleTreeExportInterface,\n options: TreeImportOptions\n): Promise<void> {\n const { reUuid, deps } = options;\n const verbose = storage.session.getDebug();\n if (verbose) printMessage(`\\n- ${treeObject.tree._id}\\n`, 'info', false);\n let newUuid = '';\n const uuidMap = {};\n const treeId = treeObject.tree._id;\n\n // Process scripts\n if (\n deps &&\n treeObject.scripts &&\n Object.entries(treeObject.scripts).length > 0\n ) {\n if (verbose) printMessage(' - Scripts:');\n for (const [scriptId, scriptObject] of Object.entries(treeObject.scripts)) {\n if (verbose)\n printMessage(\n ` - ${scriptId} (${scriptObject['name']})`,\n 'info',\n false\n );\n // is the script stored as an array of strings or just b64 blob?\n if (Array.isArray(scriptObject['script'])) {\n scriptObject['script'] = convertTextArrayToBase64(\n scriptObject['script']\n );\n } else if (!isBase64Encoded(scriptObject['script'])) {\n scriptObject['script'] = encode(JSON.parse(scriptObject['script']));\n }\n if ((await createOrUpdateScript(scriptId, scriptObject)) == null) {\n throw new Error(\n `Error importing script ${scriptObject['name']} (${scriptId}) in journey ${treeId}`\n );\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process email templates\n if (\n deps &&\n treeObject.emailTemplates &&\n Object.entries(treeObject.emailTemplates).length > 0\n ) {\n if (verbose) printMessage(' - Email templates:');\n for (const [templateId, templateData] of Object.entries(\n treeObject.emailTemplates\n )) {\n if (verbose) printMessage(` - ${templateId}`, 'info', false);\n try {\n await putEmailTemplate(templateId, templateData);\n } catch (error) {\n printMessage(error.response.data, 'error');\n throw new Error(`Error importing email templates: ${error.message}`);\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process themes\n if (deps && treeObject.themes && treeObject.themes.length > 0) {\n if (verbose) printMessage(' - Themes:');\n const themes: Map<string, ThemeSkeleton> = new Map<string, ThemeSkeleton>();\n for (const theme of treeObject.themes) {\n if (verbose)\n printMessage(` - ${theme['_id']} (${theme['name']})`, 'info');\n themes[theme['_id']] = theme;\n }\n try {\n await putThemes(themes);\n } catch (error) {\n throw new Error(`Error importing themes: ${error.message}`);\n }\n }\n\n // Process social providers\n if (\n deps &&\n treeObject.socialIdentityProviders &&\n Object.entries(treeObject.socialIdentityProviders).length > 0\n ) {\n if (verbose) printMessage(' - OAuth2/OIDC (social) identity providers:');\n for (const [providerId, providerData] of Object.entries(\n treeObject.socialIdentityProviders\n )) {\n if (verbose) printMessage(` - ${providerId}`, 'info');\n try {\n await putProviderByTypeAndId(\n providerData['_type']['_id'],\n providerId,\n providerData\n );\n } catch (importError) {\n if (\n importError.response?.status === 500 &&\n importError.response?.data?.message ===\n 'Unable to update SMS config: Data validation failed for the attribute, Redirect after form post URL'\n ) {\n providerData['redirectAfterFormPostURI'] = '';\n try {\n await putProviderByTypeAndId(\n providerData['_type']['_id'],\n providerId,\n providerData\n );\n } catch (importError2) {\n printMessage(importError.response?.data || importError, 'error');\n throw new Error(\n `Error importing provider ${providerId} in journey ${treeId}: ${importError}`\n );\n }\n } else {\n printMessage(importError.response?.data || importError, 'error');\n throw new Error(\n `\\nError importing provider ${providerId} in journey ${treeId}: ${importError}`\n );\n }\n }\n }\n }\n\n // Process saml providers\n if (\n deps &&\n treeObject.saml2Entities &&\n Object.entries(treeObject.saml2Entities).length > 0\n ) {\n if (verbose) printMessage(' - SAML2 entity providers:');\n for (const [, providerData] of Object.entries(treeObject.saml2Entities)) {\n delete providerData['_rev'];\n const entityId = providerData['entityId'];\n const entityLocation = providerData['entityLocation'];\n if (verbose) printMessage(` - ${entityLocation} ${entityId}`, 'info');\n let metaData = null;\n if (entityLocation === 'remote') {\n if (Array.isArray(providerData['base64EntityXML'])) {\n metaData = convertTextArrayToBase64Url(\n providerData['base64EntityXML']\n );\n } else {\n metaData = providerData['base64EntityXML'];\n }\n }\n delete providerData['entityLocation'];\n delete providerData['base64EntityXML'];\n // create the provider if it doesn't already exist, or just update it\n if (\n (await findProviders(`entityId eq '${entityId}'`, 'location'))\n .resultCount === 0\n ) {\n await createProvider(entityLocation, providerData, metaData).catch(\n (createProviderErr) => {\n printMessage(\n createProviderErr.response?.data || createProviderErr,\n 'error'\n );\n throw new Error(`Error creating provider ${entityId}`);\n }\n );\n } else {\n await updateProvider(entityLocation, providerData).catch(\n (updateProviderErr) => {\n printMessage(\n updateProviderErr.response?.data || updateProviderErr,\n 'error'\n );\n throw new Error(`Error updating provider ${entityId}`);\n }\n );\n }\n }\n }\n\n // Process circles of trust\n if (\n deps &&\n treeObject.circlesOfTrust &&\n Object.entries(treeObject.circlesOfTrust).length > 0\n ) {\n if (verbose) printMessage(' - SAML2 circles of trust:');\n for (const [cotId, cotData] of Object.entries(treeObject.circlesOfTrust)) {\n delete cotData['_rev'];\n if (verbose) printMessage(` - ${cotId}`, 'info');\n try {\n await createCircleOfTrust(cotData);\n } catch (createCotErr) {\n if (\n createCotErr.response?.status === 409 ||\n createCotErr.response?.status === 500\n ) {\n try {\n await updateCircleOfTrust(cotId, cotData);\n } catch (updateCotErr) {\n printMessage(createCotErr.response?.data || createCotErr, 'error');\n printMessage(updateCotErr.response?.data || updateCotErr, 'error');\n throw new Error(`Error creating/updating circle of trust ${cotId}`);\n }\n } else {\n printMessage(createCotErr.response?.data || createCotErr, 'error');\n throw new Error(`Error creating circle of trust ${cotId}`);\n }\n }\n }\n }\n\n // Process inner nodes\n let innerNodes = {};\n if (\n treeObject.innerNodes &&\n Object.entries(treeObject.innerNodes).length > 0\n ) {\n innerNodes = treeObject.innerNodes;\n }\n // old export file format\n else if (\n treeObject.innernodes &&\n Object.entries(treeObject.innernodes).length > 0\n ) {\n innerNodes = treeObject.innernodes;\n }\n if (Object.entries(innerNodes).length > 0) {\n if (verbose) printMessage(' - Inner nodes:', 'text', true);\n for (const [innerNodeId, innerNodeData] of Object.entries(innerNodes)) {\n delete innerNodeData['_rev'];\n const nodeType = innerNodeData['_type']['_id'];\n if (!reUuid) {\n newUuid = innerNodeId;\n } else {\n newUuid = uuidv4();\n uuidMap[innerNodeId] = newUuid;\n }\n innerNodeData['_id'] = newUuid;\n\n if (verbose)\n printMessage(\n ` - ${newUuid}${reUuid ? '*' : ''} (${nodeType})`,\n 'info',\n false\n );\n\n // If the node has an identityResource config setting\n // and the identityResource ends in 'user'\n // and the node's identityResource is the same as the tree's identityResource\n // change it to the current realm managed user identityResource otherwise leave it alone.\n if (\n innerNodeData['identityResource'] &&\n innerNodeData['identityResource'].endsWith('user') &&\n innerNodeData['identityResource'] === treeObject.tree.identityResource\n ) {\n innerNodeData['identityResource'] = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n `\\n - identityResource: ${innerNodeData['identityResource']}`,\n 'info',\n false\n );\n }\n try {\n await putNode(newUuid, nodeType, innerNodeData);\n } catch (nodeImportError) {\n if (\n nodeImportError.response.status === 400 &&\n nodeImportError.response.data.message ===\n 'Data validation failed for the attribute, Script'\n ) {\n throw new Error(\n `Missing script ${\n innerNodeData['script']\n } referenced by inner node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } (${innerNodeData['_type']['_id']}) in journey ${treeId}.`\n );\n } else if (\n nodeImportError.response?.status === 400 &&\n nodeImportError.response?.data?.message ===\n 'Invalid attribute specified.'\n ) {\n const { validAttributes } = nodeImportError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(innerNodeData)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete innerNodeData[attribute];\n }\n }\n try {\n await putNode(newUuid, nodeType, innerNodeData);\n } catch (nodeImportError2) {\n printMessage(nodeImportError2.response.data, 'error');\n throw new Error(\n `Error importing node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n } else {\n printMessage(nodeImportError.response.data, 'error');\n throw new Error(\n `Error importing inner node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process nodes\n if (treeObject.nodes && Object.entries(treeObject.nodes).length > 0) {\n if (verbose) printMessage(' - Nodes:');\n // eslint-disable-next-line prefer-const\n for (let [nodeId, nodeData] of Object.entries(treeObject.nodes)) {\n delete nodeData['_rev'];\n const nodeType = nodeData['_type']['_id'];\n if (!reUuid) {\n newUuid = nodeId;\n } else {\n newUuid = uuidv4();\n uuidMap[nodeId] = newUuid;\n }\n nodeData['_id'] = newUuid;\n\n if (nodeType === 'PageNode' && reUuid) {\n for (const [, inPageNodeData] of Object.entries(nodeData['nodes'])) {\n const currentId = inPageNodeData['_id'];\n nodeData = JSON.parse(\n replaceAll(JSON.stringify(nodeData), currentId, uuidMap[currentId])\n );\n }\n }\n\n if (verbose)\n printMessage(\n ` - ${newUuid}${reUuid ? '*' : ''} (${nodeType})`,\n 'info',\n false\n );\n\n // If the node has an identityResource config setting\n // and the identityResource ends in 'user'\n // and the node's identityResource is the same as the tree's identityResource\n // change it to the current realm managed user identityResource otherwise leave it alone.\n if (\n nodeData.identityResource &&\n nodeData.identityResource.endsWith('user') &&\n nodeData.identityResource === treeObject.tree.identityResource\n ) {\n nodeData['identityResource'] = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n `\\n - identityResource: ${nodeData['identityResource']}`,\n 'info',\n false\n );\n }\n try {\n await putNode(newUuid, nodeType, nodeData);\n } catch (nodeImportError) {\n if (\n nodeImportError.response.status === 400 &&\n nodeImportError.response.data.message ===\n 'Data validation failed for the attribute, Script'\n ) {\n throw new Error(\n `Missing script ${nodeData['script']} referenced by node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } (${nodeData['_type']['_id']}) in journey ${treeId}.`\n );\n } else if (\n nodeImportError.response?.status === 400 &&\n nodeImportError.response?.data?.message ===\n 'Invalid attribute specified.'\n ) {\n const { validAttributes } = nodeImportError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(nodeData)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete nodeData[attribute];\n }\n }\n try {\n await putNode(newUuid, nodeType, nodeData);\n } catch (nodeImportError2) {\n printMessage(nodeImportError2.response.data, 'error');\n throw new Error(\n `Error importing node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n } else {\n printMessage(nodeImportError.response.data, 'error');\n throw new Error(\n `Error importing node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process tree\n if (verbose) printMessage(' - Flow');\n\n if (reUuid) {\n let journeyText = JSON.stringify(treeObject.tree, null, 2);\n for (const [oldId, newId] of Object.entries(uuidMap)) {\n journeyText = replaceAll(journeyText, oldId, newId);\n }\n treeObject.tree = JSON.parse(journeyText);\n }\n\n // If the tree has an identityResource config setting\n // and the identityResource ends in 'user'\n // Set the identityResource for the tree to the selected resource.\n if (\n (treeObject.tree.identityResource &&\n (treeObject.tree['identityResource'] as string).endsWith('user')) ||\n storage.session.getDeploymentType() === global.CLOUD_DEPLOYMENT_TYPE_KEY ||\n storage.session.getDeploymentType() === global.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n treeObject.tree.identityResource = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n ` - identityResource: ${treeObject.tree.identityResource}`,\n 'info',\n false\n );\n }\n\n delete treeObject.tree._rev;\n try {\n await putTree(treeObject.tree._id as string, treeObject.tree);\n if (verbose) printMessage(`\\n - Done`, 'info', true);\n } catch (importError) {\n if (\n importError.response?.status === 400 &&\n importError.response?.data?.message === 'Invalid attribute specified.'\n ) {\n const { validAttributes } = importError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(treeObject.tree)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete treeObject.tree[attribute];\n }\n }\n try {\n await putTree(treeObject.tree._id as string, treeObject.tree);\n if (verbose) printMessage(`\\n - Done`, 'info', true);\n } catch (importError2) {\n printMessage(importError2.response.data, 'error');\n throw new Error(`Error importing journey flow ${treeId}`);\n }\n } else {\n printMessage(importError.response?.data || importError, 'error');\n debugMessage(importError.response?.data || importError);\n throw new Error(`\\nError importing journey flow ${treeId}`);\n }\n }\n}\n\n/**\n * Resolve journey dependencies\n * @param {Map} installedJorneys Map of installed journeys\n * @param {Map} journeyMap Map of journeys to resolve dependencies for\n * @param {[String]} unresolvedJourneys Map to hold the names of unresolved journeys and their dependencies\n * @param {[String]} resolvedJourneys Array to hold the names of resolved journeys\n * @param {int} index Depth of recursion\n */\nexport async function resolveDependencies(\n installedJorneys,\n journeyMap,\n unresolvedJourneys,\n resolvedJourneys,\n index = -1\n) {\n let before = -1;\n let after = index;\n if (index !== -1) {\n before = index;\n }\n\n for (const tree in journeyMap) {\n if ({}.hasOwnProperty.call(journeyMap, tree)) {\n const dependencies = [];\n for (const node in journeyMap[tree].nodes) {\n if (\n journeyMap[tree].nodes[node]._type._id === 'InnerTreeEvaluatorNode'\n ) {\n dependencies.push(journeyMap[tree].nodes[node].tree);\n }\n }\n let allResolved = true;\n for (const dependency of dependencies) {\n if (\n !resolvedJourneys.includes(dependency) &&\n !installedJorneys.includes(dependency)\n ) {\n allResolved = false;\n }\n }\n if (allResolved) {\n if (resolvedJourneys.indexOf(tree) === -1) resolvedJourneys.push(tree);\n // remove from unresolvedJourneys array\n // for (let i = 0; i < unresolvedJourneys.length; i += 1) {\n // if (unresolvedJourneys[i] === tree) {\n // unresolvedJourneys.splice(i, 1);\n // i -= 1;\n // }\n // }\n delete unresolvedJourneys[tree];\n // } else if (!unresolvedJourneys.includes(tree)) {\n } else {\n // unresolvedJourneys.push(tree);\n unresolvedJourneys[tree] = dependencies;\n }\n }\n }\n after = Object.keys(unresolvedJourneys).length;\n if (index !== -1 && after === before) {\n // This is the end, no progress was made since the last recursion\n // printMessage(\n // `Journeys with unresolved dependencies: ${unresolvedJourneys}`,\n // 'error'\n // );\n } else if (after > 0) {\n resolveDependencies(\n installedJorneys,\n journeyMap,\n unresolvedJourneys,\n resolvedJourneys,\n after\n );\n }\n}\n\n/**\n * Helper to import multiple trees from a tree map\n * @param {Object} treesMap map of trees object\n * @param {TreeImportOptions} options import options\n */\nexport async function importAllJourneys(\n treesMap: MultiTreeExportInterface,\n options: TreeImportOptions\n) {\n const installedJourneys = (await getTrees()).result.map((x) => x._id);\n const unresolvedJourneys = {};\n const resolvedJourneys = [];\n createProgressIndicator(undefined, 'Resolving dependencies', 'indeterminate');\n await resolveDependencies(\n installedJourneys,\n treesMap,\n unresolvedJourneys,\n resolvedJourneys\n );\n if (Object.keys(unresolvedJourneys).length === 0) {\n stopProgressIndicator(`Resolved all dependencies.`, 'success');\n } else {\n stopProgressIndicator(\n `${\n Object.keys(unresolvedJourneys).length\n } journeys with unresolved dependencies:`,\n 'fail'\n );\n for (const journey of Object.keys(unresolvedJourneys)) {\n printMessage(\n ` - ${journey} requires ${unresolvedJourneys[journey]}`,\n 'info'\n );\n }\n }\n createProgressIndicator(resolvedJourneys.length, 'Importing');\n for (const tree of resolvedJourneys) {\n try {\n // eslint-disable-next-line no-await-in-loop\n await importJourney(treesMap[tree], options);\n updateProgressIndicator(`${tree}`);\n } catch (error) {\n printMessage(`\\n${error.message}`, 'error');\n }\n }\n stopProgressIndicator('Done');\n}\n\n/**\n * Get the node reference obbject for a node object. Node reference objects\n * are used in a tree flow definition and within page nodes to reference\n * nodes. Among other things, node references contain all the non-configuration\n * meta data that exists for readaility, like the x/y coordinates of the node\n * and the display name chosen by the tree designer. The dislay name is the\n * only intuitive link between the graphical representation of the tree and\n * the node configurations that make up the tree.\n * @param nodeObj node object to retrieve the node reference object for\n * @param singleTreeExport tree export with or without dependencies\n * @returns {NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface} node reference object\n */\nexport function getNodeRef(\n nodeObj: NodeSkeleton,\n singleTreeExport: SingleTreeExportInterface\n): NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface {\n if (singleTreeExport.tree.nodes[nodeObj._id]) {\n return singleTreeExport.tree.nodes[nodeObj._id];\n } else {\n for (const node of Object.values(singleTreeExport.nodes)) {\n if (containerNodes.includes(node._type._id)) {\n for (const nodeRef of node.nodes) {\n if (nodeRef._id === nodeObj._id) {\n return nodeRef;\n }\n }\n }\n }\n }\n return undefined;\n}\n\n/**\n * Default tree export resolver used to resolve a tree id/name to a full export\n * w/o dependencies of that tree from a platform instance.\n * @param {string} treeId id/name of the tree to resolve\n * @returns {TreeExportResolverInterface} tree export\n */\nexport const onlineTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`onlineTreeExportResolver(${treeId})`);\n return await exportJourney(treeId, {\n deps: false,\n useStringArrays: false,\n });\n };\n\n/**\n * Tree export resolver used to resolve a tree id/name to a full export\n * of that tree from individual `treename.journey.json` export files.\n * @param {string} treeId id/name of the tree to resolve\n * @returns {TreeExportResolverInterface} tree export\n */\nexport const fileByIdTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`fileByIdTreeExportResolver(${treeId})`);\n let treeExport = createSingleTreeExportTemplate();\n const files = findFilesByName(getTypedFilename(`${treeId}`, 'journey'));\n try {\n const file = files.pop();\n const jsonData = JSON.parse(fs.readFileSync(file, 'utf8'));\n debugMessage(\n `fileByIdTreeExportResolver: resolved '${treeId}' to ${file}`\n );\n // did we resolve the tree we were asked to resolved?\n if (jsonData.tree?._id === treeId) {\n treeExport = jsonData;\n }\n // check if this is a file with multiple trees and get journey by id\n else if (jsonData.trees && jsonData.trees[treeId]) {\n treeExport = jsonData.trees[treeId];\n }\n } catch (error) {\n debugMessage(\n `fileByIdTreeExportResolver: unable to resolve '${treeId}' to a file.`\n );\n }\n return treeExport;\n };\n\n/**\n * Factory that creates a tree export resolver used to resolve a tree id\n * to a full export of that tree from a multi-tree export file.\n * @param {string} file multi-tree export file\n * @returns {TreeExportResolverInterface} tree export resolver\n */\nexport function createFileParamTreeExportResolver(\n file: string\n): TreeExportResolverInterface {\n const fileParamTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`fileParamTreeExportResolver(${treeId})`);\n let treeExport: SingleTreeExportInterface =\n createSingleTreeExportTemplate();\n try {\n const jsonData = JSON.parse(fs.readFileSync(file, 'utf8'));\n // did we resolve the tree we were asked to resolved?\n if (jsonData.tree?._id === treeId) {\n treeExport = jsonData;\n }\n // check if this is a file with multiple trees and get journey by id\n else if (jsonData.trees && jsonData.trees[treeId]) {\n treeExport = jsonData.trees[treeId];\n }\n // fall back to fileByIdTreeExportResolver\n else {\n treeExport = await fileByIdTreeExportResolver(treeId);\n }\n } catch (error) {\n //\n }\n return treeExport;\n };\n debugMessage(`fileParamTreeExportResolver: file=${file}`);\n return fileParamTreeExportResolver;\n}\n\n/**\n * Get tree dependencies (all descendent inner trees)\n * @param {SingleTreeExportInterface} treeExport single tree export\n * @param {string[]} resolvedTreeIds list of tree ids wich have already been resolved\n * @param {TreeExportResolverInterface} resolveTreeExport tree export resolver callback function\n * @returns {Promise<TreeDependencyMapInterface>} a promise that resolves to a tree dependency map\n */\nexport async function getTreeDescendents(\n treeExport: SingleTreeExportInterface,\n resolveTreeExport: TreeExportResolverInterface = onlineTreeExportResolver,\n resolvedTreeIds: string[] = []\n): Promise<TreeDependencyMapInterface> {\n debugMessage(\n `getTreeDependencies(${treeExport.tree._id}, [${resolvedTreeIds.join(\n ', '\n )}])`\n );\n if (!resolvedTreeIds.includes(treeExport.tree._id)) {\n resolvedTreeIds.push(treeExport.tree._id);\n }\n const treeDependencyMap: TreeDependencyMapInterface = {\n [treeExport.tree._id]: [],\n };\n const dependencies: TreeDependencyMapInterface[] = [];\n for (const [nodeId, node] of Object.entries(treeExport.tree.nodes)) {\n const innerTreeId = treeExport.nodes[nodeId].tree;\n if (\n node.nodeType === 'InnerTreeEvaluatorNode' &&\n !resolvedTreeIds.includes(innerTreeId)\n ) {\n const innerTreeExport = await resolveTreeExport(innerTreeId);\n debugMessage(`resolved inner tree: ${innerTreeExport.tree._id}`);\n // resolvedTreeIds.push(innerTreeId);\n dependencies.push(\n await getTreeDescendents(\n innerTreeExport,\n resolveTreeExport,\n resolvedTreeIds\n )\n );\n }\n }\n treeDependencyMap[treeExport.tree._id] = dependencies;\n return treeDependencyMap;\n}\n\n/**\n * Find all node configuration objects that are no longer referenced by any tree\n * @returns {Promise<unknown[]>} a promise that resolves to an array of orphaned nodes\n */\nexport async function findOrphanedNodes(): Promise<unknown[]> {\n const allNodes = [];\n const orphanedNodes = [];\n let types = [];\n const allJourneys = (await getTrees()).result;\n let errorMessage = '';\n const errorTypes = [];\n\n createProgressIndicator(\n undefined,\n `Counting total nodes...`,\n 'indeterminate'\n );\n try {\n types = (await getNodeTypes()).result;\n } catch (error) {\n printMessage('Error retrieving all available node types:', 'error');\n printMessage(error.response.data, 'error');\n return [];\n }\n for (const type of types) {\n try {\n // eslint-disable-next-line no-await-in-loop, no-loop-func\n const nodes = (await getNodesByType(type._id)).result;\n for (const node of nodes) {\n allNodes.push(node);\n updateProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`\n );\n }\n } catch (error) {\n errorTypes.push(type._id);\n errorMessage = ` (Skipped type(s): ${errorTypes})`['yellow'];\n updateProgressIndicator(`${allNodes.length} total nodes${errorMessage}`);\n }\n }\n if (errorTypes.length > 0) {\n stopProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`,\n 'warn'\n );\n } else {\n stopProgressIndicator(`${allNodes.length} total nodes`, 'success');\n }\n\n createProgressIndicator(\n undefined,\n 'Counting active nodes...',\n 'indeterminate'\n );\n const activeNodes = [];\n for (const journey of allJourneys) {\n for (const nodeId in journey.nodes) {\n if ({}.hasOwnProperty.call(journey.nodes, nodeId)) {\n activeNodes.push(nodeId);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n const node = journey.nodes[nodeId];\n if (containerNodes.includes(node.nodeType)) {\n const containerNode = await getNode(nodeId, node.nodeType);\n for (const innerNode of containerNode.nodes) {\n activeNodes.push(innerNode._id);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n }\n }\n }\n }\n }\n stopProgressIndicator(`${activeNodes.length} active nodes`, 'success');\n\n createProgressIndicator(\n undefined,\n 'Calculating orphaned nodes...',\n 'indeterminate'\n );\n const diff = allNodes.filter((x) => !activeNodes.includes(x._id));\n for (const orphanedNode of diff) {\n orphanedNodes.push(orphanedNode);\n }\n stopProgressIndicator(`${orphanedNodes.length} orphaned nodes`, 'success');\n return orphanedNodes;\n}\n\n/**\n * Remove orphaned nodes\n * @param {[Object]} orphanedNodes Pass in an array of orphaned node configuration objects to remove\n * @returns {Promise<unknown[]>} a promise that resolves to an array nodes that encountered errors deleting\n */\nexport async function removeOrphanedNodes(\n orphanedNodes: unknown[]\n): Promise<unknown[]> {\n const errorNodes = [];\n createProgressIndicator(orphanedNodes.length, 'Removing orphaned nodes...');\n for (const node of orphanedNodes) {\n updateProgressIndicator(`Removing ${node['_id']}...`);\n try {\n // eslint-disable-next-line no-await-in-loop\n await deleteNode(node['_id'], node['_type']['_id']);\n } catch (deleteError) {\n errorNodes.push(node);\n printMessage(` ${deleteError}`, 'error');\n }\n }\n stopProgressIndicator(`Removed ${orphanedNodes.length} orphaned nodes.`);\n return errorNodes;\n}\n\n/**\n * Analyze if a journey contains any custom nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.\n */\nexport function isCustomJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isCustomNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Analyze if a journey contains any premium nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.\n */\nexport function isPremiumJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isPremiumNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Analyze if a journey contains any cloud-only nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any cloud-only nodes, false otherwise.\n */\nexport function isCloudOnlyJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isCloudOnlyNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Get a journey's classifications, which can be one or multiple of:\n * - standard: can run on any instance of a ForgeRock platform\n * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud\n * - premium: utilizes nodes, which come at a premium\n * - custom: utilizes nodes not included in the ForgeRock platform release\n * @param {SingleTreeExportInterface} journey journey export data\n * @returns {JourneyClassification[]} an array of one or multiple classifications\n */\nexport function getJourneyClassification(\n journey: SingleTreeExportInterface\n): JourneyClassification[] {\n const classifications: JourneyClassification[] = [];\n const premium = isPremiumJourney(journey);\n const custom = isCustomJourney(journey);\n const cloud = isCloudOnlyJourney(journey);\n if (custom) {\n classifications.push(JourneyClassification.CUSTOM);\n } else if (cloud) {\n classifications.push(JourneyClassification.CLOUD);\n } else {\n classifications.push(JourneyClassification.STANDARD);\n }\n if (premium) classifications.push(JourneyClassification.PREMIUM);\n return classifications;\n}\n\n/**\n * Delete a journey\n * @param {string} journeyId journey id/name\n * @param {Object} options deep=true also delete all the nodes and inner nodes, verbose=true print verbose info\n */\nexport async function deleteJourney(\n journeyId: string,\n options,\n progress = true\n) {\n const { deep } = options;\n const { verbose } = options;\n const status = { nodes: {} };\n if (progress)\n createProgressIndicator(\n undefined,\n `Deleting ${journeyId}...`,\n 'indeterminate'\n );\n if (progress && verbose) stopProgressIndicator();\n return deleteTree(journeyId)\n .then(async (deleteTreeResponse) => {\n status['status'] = 'success';\n const nodePromises = [];\n if (verbose) printMessage(`Deleted ${journeyId} (tree)`, 'info');\n if (deep) {\n for (const [nodeId, nodeObject] of Object.entries(\n deleteTreeResponse.nodes\n )) {\n // delete inner nodes (nodes inside container nodes)\n if (containerNodes.includes(nodeObject['nodeType'])) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const containerNode = await getNode(\n nodeId,\n nodeObject['nodeType']\n );\n if (verbose)\n printMessage(\n `Read ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}`,\n 'info'\n );\n for (const innerNodeObject of containerNode.nodes) {\n nodePromises.push(\n deleteNode(innerNodeObject._id, innerNodeObject.nodeType)\n .then((response2) => {\n status.nodes[innerNodeObject._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${innerNodeObject._id} (${innerNodeObject.nodeType}) from ${journeyId}`,\n 'info'\n );\n return response2;\n })\n .catch((error) => {\n status.nodes[innerNodeObject._id] = {\n status: 'error',\n error,\n };\n if (verbose)\n printMessage(\n `Error deleting inner node ${innerNodeObject._id} (${innerNodeObject.nodeType}) from ${journeyId}: ${error}`,\n 'error'\n );\n })\n );\n }\n // finally delete the container node\n nodePromises.push(\n deleteNode(containerNode._id, containerNode['_type']['_id'])\n .then((response2) => {\n status.nodes[containerNode._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}`,\n 'info'\n );\n return response2;\n })\n .catch((error) => {\n if (\n error?.response?.data?.code === 500 &&\n error.response.data.message ===\n 'Unable to read SMS config: Node did not exist'\n ) {\n status.nodes[containerNode._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}`,\n 'info'\n );\n } else {\n status.nodes[containerNode._id] = {\n status: 'error',\n error,\n };\n if (verbose)\n printMessage(\n `Error deleting container node ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}: ${error.response.data.message}`,\n 'error'\n );\n }\n })\n );\n } catch (error) {\n if (verbose)\n printMessage(\n `Error getting container node ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}: ${error}`,\n 'error'\n );\n }\n } else {\n // delete the node\n nodePromises.push(\n deleteNode(nodeId, nodeObject['nodeType'])\n .then((response) => {\n status.nodes[nodeId] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}`,\n 'info'\n );\n return response;\n })\n .catch((error) => {\n status.nodes[nodeId] = { status: 'error', error };\n if (verbose)\n printMessage(\n `Error deleting node ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}: ${error}`,\n 'error'\n );\n })\n );\n }\n }\n }\n // wait until all the node calls are complete\n await Promise.allSettled(nodePromises);\n\n // report status\n if (progress) {\n let nodeCount = 0;\n let errorCount = 0;\n for (const node of Object.keys(status.nodes)) {\n nodeCount += 1;\n if (status.nodes[node].status === 'error') errorCount += 1;\n }\n if (errorCount === 0) {\n stopProgressIndicator(\n `Deleted ${journeyId} and ${\n nodeCount - errorCount\n }/${nodeCount} nodes.`,\n 'success'\n );\n } else {\n stopProgressIndicator(\n `Deleted ${journeyId} and ${\n nodeCount - errorCount\n }/${nodeCount} nodes.`,\n 'fail'\n );\n }\n }\n return status;\n })\n .catch((error) => {\n status['status'] = 'error';\n status['error'] = error;\n stopProgressIndicator(`Error deleting ${journeyId}.`, 'fail');\n if (verbose)\n printMessage(`Error deleting tree ${journeyId}: ${error}`, 'error');\n return status;\n });\n}\n\n/**\n * Delete all journeys\n * @param {Object} options deep=true also delete all the nodes and inner nodes, verbose=true print verbose info\n */\nexport async function deleteJourneys(options) {\n const { verbose } = options;\n const status = {};\n const trees = (await getTrees()).result;\n createProgressIndicator(trees.length, 'Deleting journeys...');\n for (const tree of trees) {\n if (verbose) printMessage('');\n // eslint-disable-next-line no-await-in-loop\n status[tree._id] = await deleteJourney(tree._id, options, false);\n updateProgressIndicator(`${tree._id}`);\n // introduce a 100ms wait to allow the progress bar to update before the next verbose message prints from the async function\n if (verbose)\n // eslint-disable-next-line no-await-in-loop\n await new Promise((r) => {\n setTimeout(r, 100);\n });\n }\n let journeyCount = 0;\n let journeyErrorCount = 0;\n let nodeCount = 0;\n let nodeErrorCount = 0;\n for (const journey of Object.keys(status)) {\n journeyCount += 1;\n if (status[journey].status === 'error') journeyErrorCount += 1;\n for (const node of Object.keys(status[journey].nodes)) {\n nodeCount += 1;\n if (status[journey].nodes[node].status === 'error') nodeErrorCount += 1;\n }\n }\n stopProgressIndicator(\n `Deleted ${journeyCount - journeyErrorCount}/${journeyCount} journeys and ${\n nodeCount - nodeErrorCount\n }/${nodeCount} nodes.`\n );\n return status;\n}\n\n/**\n * Enable a journey\n * @param journeyId journey id/name\n * @returns {Promise<boolean>} true if the operation was successful, false otherwise\n */\nexport async function enableJourney(journeyId: string): Promise<boolean> {\n try {\n const treeObject = await getTree(journeyId);\n treeObject['enabled'] = true;\n delete treeObject._rev;\n const newTreeObject = await putTree(journeyId, treeObject);\n return newTreeObject['enabled'] === true;\n } catch (error) {\n printMessage(error.response.data, 'error');\n return false;\n }\n}\n\n/**\n * Disable a journey\n * @param journeyId journey id/name\n * @returns {Promise<boolean>} true if the operation was successful, false otherwise\n */\nexport async function disableJourney(journeyId: string): Promise<boolean> {\n try {\n const treeObject = await getTree(journeyId);\n treeObject['enabled'] = false;\n delete treeObject._rev;\n const newTreeObject = await putTree(journeyId, treeObject);\n return newTreeObject['enabled'] === false;\n } catch (error) {\n printMessage(error.response.data, 'error');\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/JourneyOps.ts"],"names":[],"mappings":"AAwDA,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAiB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAgBpB;;;GAGG;AACH,wBAAgB,8BAA8B,IAAI,yBAAyB,CAa1E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,wBAAwB,CAKxE;AAsFD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,iBAGR,GACA,OAAO,CAAC,yBAAyB,CAAC,CAmXpC;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAU3D;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,yBAAyB,EACrC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAgef;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,gBAAgB,KAAA,EAChB,UAAU,KAAA,EACV,kBAAkB,KAAA,EAClB,gBAAgB,KAAA,EAChB,KAAK,SAAK,iBA4DX;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,iBAAiB,iBAuC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,gBAAgB,EAAE,yBAAyB,GAC1C,wBAAwB,GAAG,6BAA6B,CAe1D;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,2BAOpC,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,2BAyBtC,CAAC;AAEJ;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,GACX,2BAA2B,CA2B7B;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,yBAAyB,EACrC,iBAAiB,GAAE,2BAAsD,EACzE,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CAiCrC;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAgF5D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,OAAO,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAepB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,yBAAyB,WAUjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,WAUlE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,WAUpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,yBAAyB,GACjC,qBAAqB,EAAE,CAczB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,KAAA,EACP,QAAQ,UAAO;;GAqKhB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,KAAA,eAmC3C;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWxE","file":"JourneyOps.d.ts","sourcesContent":["import fs from 'fs';\nimport { v4 as uuidv4 } from 'uuid';\nimport _ from 'lodash';\nimport {\n convertBase64TextToArray,\n getTypedFilename,\n convertTextArrayToBase64,\n convertTextArrayToBase64Url,\n findFilesByName,\n} from './utils/ExportImportUtils';\nimport { getRealmManagedUser, replaceAll } from './utils/OpsUtils';\nimport * as state from '../shared/State';\nimport {\n getNode,\n putNode,\n deleteNode,\n getNodeTypes,\n getNodesByType,\n} from '../api/NodeApi';\nimport { isCloudOnlyNode, isCustomNode, isPremiumNode } from './NodeOps';\nimport { getTrees, getTree, putTree, deleteTree } from '../api/TreeApi';\nimport { getEmailTemplate, putEmailTemplate } from './EmailTemplateOps';\nimport { getScript } from '../api/ScriptApi';\nimport * as globalConfig from '../storage/StaticStorage';\nimport {\n printMessage,\n createProgressIndicator,\n updateProgressIndicator,\n stopProgressIndicator,\n debugMessage,\n} from './utils/Console';\nimport {\n getProviderByLocationAndId,\n getProviders,\n getProviderMetadata,\n createProvider,\n findProviders,\n updateProvider,\n} from '../api/Saml2Api';\nimport {\n createCircleOfTrust,\n getCirclesOfTrust,\n updateCircleOfTrust,\n} from '../api/CirclesOfTrustApi';\nimport {\n decode,\n encode,\n encodeBase64Url,\n isBase64Encoded,\n} from '../api/utils/Base64';\nimport {\n getSocialIdentityProviders,\n putProviderByTypeAndId,\n} from '../api/SocialIdentityProvidersApi';\nimport { getThemes, putThemes } from './ThemeOps';\nimport { createOrUpdateScript } from './ScriptOps';\nimport { JourneyClassification, TreeExportResolverInterface } from './OpsTypes';\nimport { ThemeSkeleton, TreeSkeleton } from '../api/ApiTypes';\nimport {\n InnerNodeRefSkeletonInterface,\n NodeRefSkeletonInterface,\n NodeSkeleton,\n} from '../api/ApiTypes';\nimport {\n SingleTreeExportInterface,\n MultiTreeExportInterface,\n TreeDependencyMapInterface,\n TreeExportOptions,\n TreeImportOptions,\n} from './OpsTypes';\n\nconst containerNodes = ['PageNode', 'CustomPageNode'];\n\nconst scriptedNodes = [\n 'ConfigProviderNode',\n 'ScriptedDecisionNode',\n 'ClientScriptNode',\n 'SocialProviderHandlerNode',\n 'CustomScriptNode',\n];\n\nconst emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];\n\nconst emptyScriptPlaceholder = '[Empty]';\n\n/**\n * Create an empty single tree export template\n * @returns {SingleTreeExportInterface} an empty single tree export template\n */\nexport function createSingleTreeExportTemplate(): SingleTreeExportInterface {\n return {\n meta: {},\n innerNodes: {},\n nodes: {},\n scripts: {},\n emailTemplates: {},\n socialIdentityProviders: {},\n themes: [],\n saml2Entities: {},\n circlesOfTrust: {},\n tree: {},\n } as SingleTreeExportInterface;\n}\n\n/**\n * Create an empty multi tree export template\n * @returns {MultiTreeExportInterface} an empty multi tree export template\n */\nexport function createMultiTreeExportTemplate(): MultiTreeExportInterface {\n return {\n meta: {},\n trees: {},\n } as MultiTreeExportInterface;\n}\n\n/**\n * Helper to get all SAML2 dependencies for a given node object\n * @param {Object} nodeObject node object\n * @param {[Object]} allProviders array of all saml2 providers objects\n * @param {[Object]} allCirclesOfTrust array of all circle of trust objects\n * @returns {Promise} a promise that resolves to an object containing a saml2 dependencies\n */\nasync function getSaml2NodeDependencies(\n nodeObject,\n allProviders,\n allCirclesOfTrust\n) {\n const samlProperties = ['metaAlias', 'idpEntityId'];\n const saml2EntityPromises = [];\n for (const samlProperty of samlProperties) {\n // In the following line nodeObject[samlProperty] will look like '/alpha/iSPAzure'.\n const entityId =\n samlProperty === 'metaAlias'\n ? _.last(nodeObject[samlProperty].split('/'))\n : nodeObject[samlProperty];\n const entity = _.find(allProviders, { entityId });\n if (entity) {\n try {\n const providerResponse = await getProviderByLocationAndId(\n entity.location,\n entity._id\n );\n /**\n * Adding entityLocation here to the entityResponse because the import tool\n * needs to know whether the saml2 entity is remote or not (this will be removed\n * from the config before importing see updateSaml2Entity and createSaml2Entity functions).\n * Importing a remote saml2 entity is a slightly different request (see createSaml2Entity).\n */\n providerResponse.entityLocation = entity.location;\n\n if (entity.location === 'remote') {\n // get the xml representation of this entity and add it to the entityResponse;\n const metaDataResponse = await getProviderMetadata(\n providerResponse.entityId\n );\n providerResponse.base64EntityXML = encodeBase64Url(metaDataResponse);\n }\n saml2EntityPromises.push(providerResponse);\n } catch (error) {\n printMessage(error.message, 'error');\n }\n }\n }\n try {\n const saml2EntitiesPromisesResults = await Promise.all(saml2EntityPromises);\n const saml2Entities = [];\n for (const saml2Entity of saml2EntitiesPromisesResults) {\n if (saml2Entity) {\n saml2Entities.push(saml2Entity);\n }\n }\n const samlEntityIds = _.map(\n saml2Entities,\n (saml2EntityConfig) => `${saml2EntityConfig.entityId}|saml2`\n );\n const circlesOfTrust = _.filter(allCirclesOfTrust, (circleOfTrust) => {\n let hasEntityId = false;\n for (const trustedProvider of circleOfTrust.trustedProviders) {\n if (!hasEntityId && samlEntityIds.includes(trustedProvider)) {\n hasEntityId = true;\n }\n }\n return hasEntityId;\n });\n const saml2NodeDependencies = {\n saml2Entities,\n circlesOfTrust,\n };\n return saml2NodeDependencies;\n } catch (error) {\n printMessage(error.message, 'error');\n const saml2NodeDependencies = {\n saml2Entities: [],\n circlesOfTrust: [],\n };\n return saml2NodeDependencies;\n }\n}\n\n/**\n * Create export data for a tree/journey with all its nodes and dependencies. The export data can be written to a file as is.\n * @param {string} treeId tree id/name\n * @param {TreeExportOptions} options export options\n * @returns {Promise<SingleTreeExportInterface>} a promise that resolves to an object containing the tree and all its nodes and dependencies\n */\nexport async function exportJourney(\n treeId: string,\n options: TreeExportOptions = {\n useStringArrays: true,\n deps: true,\n }\n): Promise<SingleTreeExportInterface> {\n const exportData = createSingleTreeExportTemplate();\n try {\n const treeObject = await getTree(treeId);\n const { useStringArrays, deps } = options;\n const verbose = state.getDebug();\n\n if (verbose) printMessage(`\\n- ${treeObject._id}\\n`, 'info', false);\n\n // Process tree\n if (verbose) printMessage(' - Flow');\n exportData.tree = treeObject;\n if (verbose && treeObject.identityResource)\n printMessage(\n ` - identityResource: ${treeObject.identityResource}`,\n 'info'\n );\n if (verbose) printMessage(` - Done`, 'info');\n\n const nodePromises = [];\n const scriptPromises = [];\n const emailTemplatePromises = [];\n const innerNodePromises = [];\n const saml2ConfigPromises = [];\n let socialProviderPromise = null;\n let themePromise = null;\n if (\n deps &&\n state.getDeploymentType() !== globalConfig.CLASSIC_DEPLOYMENT_TYPE_KEY\n ) {\n try {\n themePromise = getThemes();\n } catch (error) {\n printMessage(error, 'error');\n }\n }\n\n let allSaml2Providers = null;\n let allCirclesOfTrust = null;\n let filteredSocialProviders = null;\n const themes = [];\n\n // get all the nodes\n for (const [nodeId, nodeInfo] of Object.entries(treeObject.nodes)) {\n nodePromises.push(getNode(nodeId, nodeInfo['nodeType']));\n }\n if (verbose && nodePromises.length > 0) printMessage(' - Nodes:');\n const nodeObjects = await Promise.all(nodePromises);\n\n // iterate over every node in tree\n for (const nodeObject of nodeObjects) {\n const nodeId = nodeObject._id;\n const nodeType = nodeObject._type._id;\n if (verbose) printMessage(` - ${nodeId} (${nodeType})`, 'info', true);\n exportData.nodes[nodeObject._id] = nodeObject;\n\n // handle script node types\n if (\n deps &&\n scriptedNodes.includes(nodeType) &&\n nodeObject.script !== emptyScriptPlaceholder\n ) {\n scriptPromises.push(getScript(nodeObject.script));\n }\n\n // frodo supports email templates in platform deployments\n if (\n (deps &&\n state.getDeploymentType() ===\n globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n state.getDeploymentType() === globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n if (emailTemplateNodes.includes(nodeType)) {\n try {\n const emailTemplate = await getEmailTemplate(\n nodeObject.emailTemplateName\n );\n emailTemplatePromises.push(emailTemplate);\n } catch (error) {\n let message = `${error}`;\n if (error.isAxiosError && error.response.status) {\n message = error.response.statusText;\n }\n printMessage(\n `\\n${message}: Email Template \"${nodeObject.emailTemplateName}\"`,\n 'error'\n );\n }\n }\n }\n\n // handle SAML2 node dependencies\n if (deps && nodeType === 'product-Saml2Node') {\n if (!allSaml2Providers) {\n // eslint-disable-next-line no-await-in-loop\n allSaml2Providers = (await getProviders()).result;\n }\n if (!allCirclesOfTrust) {\n // eslint-disable-next-line no-await-in-loop\n allCirclesOfTrust = (await getCirclesOfTrust()).result;\n }\n saml2ConfigPromises.push(\n getSaml2NodeDependencies(\n nodeObject,\n allSaml2Providers,\n allCirclesOfTrust\n )\n );\n }\n\n // If this is a SocialProviderHandlerNode get each enabled social identity provider.\n if (\n deps &&\n !socialProviderPromise &&\n nodeType === 'SocialProviderHandlerNode'\n ) {\n socialProviderPromise = getSocialIdentityProviders();\n }\n\n // If this is a SelectIdPNode and filteredProviters is not already set to empty array set filteredSocialProviers.\n if (deps && !filteredSocialProviders && nodeType === 'SelectIdPNode') {\n filteredSocialProviders = filteredSocialProviders || [];\n for (const filteredProvider of nodeObject.filteredProviders) {\n if (!filteredSocialProviders.includes(filteredProvider)) {\n filteredSocialProviders.push(filteredProvider);\n }\n }\n }\n\n // get inner nodes (nodes inside container nodes)\n if (containerNodes.includes(nodeType)) {\n for (const innerNode of nodeObject.nodes) {\n innerNodePromises.push(getNode(innerNode._id, innerNode.nodeType));\n }\n // frodo supports themes in platform deployments\n if (\n (deps &&\n state.getDeploymentType() ===\n globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n state.getDeploymentType() ===\n globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n let themeId = false;\n\n if (nodeObject.stage) {\n // see if themeId is part of the stage object\n try {\n themeId = JSON.parse(nodeObject.stage).themeId;\n } catch (e) {\n themeId = false;\n }\n // if the page node's themeId is set the \"old way\" set themeId accordingly\n if (!themeId && nodeObject.stage.indexOf('themeId=') === 0) {\n // eslint-disable-next-line prefer-destructuring\n themeId = nodeObject.stage.split('=')[1];\n }\n }\n\n if (themeId) {\n if (!themes.includes(themeId)) themes.push(themeId);\n }\n }\n }\n }\n\n // Process inner nodes\n if (verbose && innerNodePromises.length > 0)\n printMessage(' - Inner nodes:');\n const innerNodeDataResults = await Promise.all(innerNodePromises);\n for (const innerNodeObject of innerNodeDataResults) {\n const innerNodeId = innerNodeObject._id;\n const innerNodeType = innerNodeObject._type._id;\n if (verbose)\n printMessage(` - ${innerNodeId} (${innerNodeType})`, 'info', true);\n exportData.innerNodes[innerNodeId] = innerNodeObject;\n\n // handle script node types\n if (deps && scriptedNodes.includes(innerNodeType)) {\n scriptPromises.push(getScript(innerNodeObject.script));\n }\n\n // frodo supports email templates in platform deployments\n if (\n (deps &&\n state.getDeploymentType() ===\n globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY) ||\n state.getDeploymentType() === globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n if (emailTemplateNodes.includes(innerNodeType)) {\n try {\n const emailTemplate = await getEmailTemplate(\n innerNodeObject.emailTemplateName\n );\n emailTemplatePromises.push(emailTemplate);\n } catch (error) {\n let message = `${error}`;\n if (error.isAxiosError && error.response.status) {\n message = error.response.statusText;\n }\n printMessage(\n `\\n${message}: Email Template \"${innerNodeObject.emailTemplateName}\"`,\n 'error'\n );\n }\n }\n }\n\n // handle SAML2 node dependencies\n if (deps && innerNodeType === 'product-Saml2Node') {\n printMessage('SAML2 inner node', 'error');\n if (!allSaml2Providers) {\n // eslint-disable-next-line no-await-in-loop\n allSaml2Providers = (await getProviders()).result;\n }\n if (!allCirclesOfTrust) {\n // eslint-disable-next-line no-await-in-loop\n allCirclesOfTrust = (await getCirclesOfTrust()).result;\n }\n saml2ConfigPromises.push(\n getSaml2NodeDependencies(\n innerNodeObject,\n allSaml2Providers,\n allCirclesOfTrust\n )\n );\n }\n\n // If this is a SocialProviderHandlerNode get each enabled social identity provider.\n if (\n deps &&\n !socialProviderPromise &&\n innerNodeType === 'SocialProviderHandlerNode'\n ) {\n socialProviderPromise = getSocialIdentityProviders();\n }\n\n // If this is a SelectIdPNode and filteredProviters is not already set to empty array set filteredSocialProviers.\n if (\n deps &&\n !filteredSocialProviders &&\n innerNodeType === 'SelectIdPNode' &&\n innerNodeObject.filteredProviders\n ) {\n filteredSocialProviders = filteredSocialProviders || [];\n for (const filteredProvider of innerNodeObject.filteredProviders) {\n if (!filteredSocialProviders.includes(filteredProvider)) {\n filteredSocialProviders.push(filteredProvider);\n }\n }\n }\n }\n\n // Process email templates\n if (verbose && emailTemplatePromises.length > 0)\n printMessage(' - Email templates:');\n const settledEmailTemplatePromises = await Promise.allSettled(\n emailTemplatePromises\n );\n for (const settledPromise of settledEmailTemplatePromises) {\n if (settledPromise.status === 'fulfilled' && settledPromise.value) {\n if (verbose)\n printMessage(\n ` - ${settledPromise.value._id.split('/')[1]}${\n settledPromise.value.displayName\n ? ` (${settledPromise.value.displayName})`\n : ''\n }`,\n 'info',\n true\n );\n exportData.emailTemplates[settledPromise.value._id.split('/')[1]] =\n settledPromise.value;\n }\n }\n\n // Process SAML2 providers and circles of trust\n const saml2NodeDependencies = await Promise.all(saml2ConfigPromises);\n for (const saml2NodeDependency of saml2NodeDependencies) {\n if (saml2NodeDependency) {\n if (verbose) printMessage(' - SAML2 entity providers:');\n for (const saml2Entity of saml2NodeDependency.saml2Entities) {\n if (verbose)\n printMessage(\n ` - ${saml2Entity.entityLocation} ${saml2Entity.entityId}`,\n 'info'\n );\n exportData.saml2Entities[saml2Entity._id] = saml2Entity;\n }\n if (verbose) printMessage(' - SAML2 circles of trust:');\n for (const circleOfTrust of saml2NodeDependency.circlesOfTrust) {\n if (verbose) printMessage(` - ${circleOfTrust._id}`, 'info');\n exportData.circlesOfTrust[circleOfTrust._id] = circleOfTrust;\n }\n }\n }\n\n // Process socialIdentityProviders\n const socialProviders = await Promise.resolve(socialProviderPromise);\n if (socialProviders) {\n if (verbose) printMessage(' - OAuth2/OIDC (social) identity providers:');\n for (const socialProvider of socialProviders.result) {\n // If the list of socialIdentityProviders needs to be filtered based on the\n // filteredProviders property of a SelectIdPNode do it here.\n if (\n socialProvider &&\n (!filteredSocialProviders ||\n filteredSocialProviders.length === 0 ||\n filteredSocialProviders.includes(socialProvider._id))\n ) {\n if (verbose) printMessage(` - ${socialProvider._id}`, 'info');\n scriptPromises.push(getScript(socialProvider.transform));\n exportData.socialIdentityProviders[socialProvider._id] =\n socialProvider;\n }\n }\n }\n\n // Process scripts\n if (verbose && scriptPromises.length > 0) printMessage(' - Scripts:');\n const scriptObjects = await Promise.all(scriptPromises);\n for (const scriptObject of scriptObjects) {\n if (scriptObject) {\n if (verbose)\n printMessage(\n ` - ${scriptObject._id} (${scriptObject.name})`,\n 'info',\n true\n );\n scriptObject.script = useStringArrays\n ? convertBase64TextToArray(scriptObject.script)\n : JSON.stringify(decode(scriptObject.script));\n exportData.scripts[scriptObject._id] = scriptObject;\n }\n }\n\n // Process themes\n if (themePromise) {\n if (verbose) printMessage(' - Themes:');\n try {\n const themePromiseResults = await Promise.resolve(themePromise);\n for (const themeObject of themePromiseResults) {\n if (\n themeObject &&\n // has the theme been specified by id or name in a page node?\n (themes.includes(themeObject._id) ||\n themes.includes(themeObject.name) ||\n // has this journey been linked to a theme?\n themeObject.linkedTrees?.includes(treeObject._id))\n ) {\n if (verbose)\n printMessage(\n ` - ${themeObject._id} (${themeObject.name})`,\n 'info'\n );\n exportData.themes.push(themeObject);\n }\n }\n } catch (error) {\n printMessage(error.response.data, 'error');\n printMessage('Error handling themes: ' + error.message, 'error');\n }\n }\n } catch (error) {\n printMessage(error.response.data, 'error');\n printMessage(\n 'Error exporting tree: ' + treeId + ' - ' + error.message,\n 'error'\n );\n }\n\n return exportData;\n}\n\n/**\n * Get all the journeys/trees without all their nodes and dependencies.\n * @returns {Promise<TreeSkeleton[]>} a promise that resolves to an array of journey objects\n */\nexport async function getJourneys(): Promise<TreeSkeleton[]> {\n let journeys = [];\n try {\n journeys = (await getTrees()).result;\n } catch (error) {\n printMessage(`${error.message}`, 'error');\n printMessage(error.response.data, 'error');\n }\n journeys.sort((a, b) => a._id.localeCompare(b._id));\n return journeys;\n}\n\n/**\n * Helper to import a tree with all dependencies from a `SingleTreeExportInterface` object (typically read from a file)\n * @param {SingleTreeExportInterface} treeObject tree object containing tree and all its dependencies\n * @param {TreeImportOptions} options import options\n */\nexport async function importJourney(\n treeObject: SingleTreeExportInterface,\n options: TreeImportOptions\n): Promise<void> {\n const { reUuid, deps } = options;\n const verbose = state.getDebug();\n if (verbose) printMessage(`\\n- ${treeObject.tree._id}\\n`, 'info', false);\n let newUuid = '';\n const uuidMap = {};\n const treeId = treeObject.tree._id;\n\n // Process scripts\n if (\n deps &&\n treeObject.scripts &&\n Object.entries(treeObject.scripts).length > 0\n ) {\n if (verbose) printMessage(' - Scripts:');\n for (const [scriptId, scriptObject] of Object.entries(treeObject.scripts)) {\n if (verbose)\n printMessage(\n ` - ${scriptId} (${scriptObject['name']})`,\n 'info',\n false\n );\n // is the script stored as an array of strings or just b64 blob?\n if (Array.isArray(scriptObject['script'])) {\n scriptObject['script'] = convertTextArrayToBase64(\n scriptObject['script']\n );\n } else if (!isBase64Encoded(scriptObject['script'])) {\n scriptObject['script'] = encode(JSON.parse(scriptObject['script']));\n }\n if ((await createOrUpdateScript(scriptId, scriptObject)) == null) {\n throw new Error(\n `Error importing script ${scriptObject['name']} (${scriptId}) in journey ${treeId}`\n );\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process email templates\n if (\n deps &&\n treeObject.emailTemplates &&\n Object.entries(treeObject.emailTemplates).length > 0\n ) {\n if (verbose) printMessage(' - Email templates:');\n for (const [templateId, templateData] of Object.entries(\n treeObject.emailTemplates\n )) {\n if (verbose) printMessage(` - ${templateId}`, 'info', false);\n try {\n await putEmailTemplate(templateId, templateData);\n } catch (error) {\n printMessage(error.response.data, 'error');\n throw new Error(`Error importing email templates: ${error.message}`);\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process themes\n if (deps && treeObject.themes && treeObject.themes.length > 0) {\n if (verbose) printMessage(' - Themes:');\n const themes: Map<string, ThemeSkeleton> = new Map<string, ThemeSkeleton>();\n for (const theme of treeObject.themes) {\n if (verbose)\n printMessage(` - ${theme['_id']} (${theme['name']})`, 'info');\n themes[theme['_id']] = theme;\n }\n try {\n await putThemes(themes);\n } catch (error) {\n throw new Error(`Error importing themes: ${error.message}`);\n }\n }\n\n // Process social providers\n if (\n deps &&\n treeObject.socialIdentityProviders &&\n Object.entries(treeObject.socialIdentityProviders).length > 0\n ) {\n if (verbose) printMessage(' - OAuth2/OIDC (social) identity providers:');\n for (const [providerId, providerData] of Object.entries(\n treeObject.socialIdentityProviders\n )) {\n if (verbose) printMessage(` - ${providerId}`, 'info');\n try {\n await putProviderByTypeAndId(\n providerData['_type']['_id'],\n providerId,\n providerData\n );\n } catch (importError) {\n if (\n importError.response?.status === 500 &&\n importError.response?.data?.message ===\n 'Unable to update SMS config: Data validation failed for the attribute, Redirect after form post URL'\n ) {\n providerData['redirectAfterFormPostURI'] = '';\n try {\n await putProviderByTypeAndId(\n providerData['_type']['_id'],\n providerId,\n providerData\n );\n } catch (importError2) {\n printMessage(importError.response?.data || importError, 'error');\n throw new Error(\n `Error importing provider ${providerId} in journey ${treeId}: ${importError}`\n );\n }\n } else {\n printMessage(importError.response?.data || importError, 'error');\n throw new Error(\n `\\nError importing provider ${providerId} in journey ${treeId}: ${importError}`\n );\n }\n }\n }\n }\n\n // Process saml providers\n if (\n deps &&\n treeObject.saml2Entities &&\n Object.entries(treeObject.saml2Entities).length > 0\n ) {\n if (verbose) printMessage(' - SAML2 entity providers:');\n for (const [, providerData] of Object.entries(treeObject.saml2Entities)) {\n delete providerData['_rev'];\n const entityId = providerData['entityId'];\n const entityLocation = providerData['entityLocation'];\n if (verbose) printMessage(` - ${entityLocation} ${entityId}`, 'info');\n let metaData = null;\n if (entityLocation === 'remote') {\n if (Array.isArray(providerData['base64EntityXML'])) {\n metaData = convertTextArrayToBase64Url(\n providerData['base64EntityXML']\n );\n } else {\n metaData = providerData['base64EntityXML'];\n }\n }\n delete providerData['entityLocation'];\n delete providerData['base64EntityXML'];\n // create the provider if it doesn't already exist, or just update it\n if (\n (await findProviders(`entityId eq '${entityId}'`, ['location']))\n .resultCount === 0\n ) {\n await createProvider(entityLocation, providerData, metaData).catch(\n (createProviderErr) => {\n printMessage(\n createProviderErr.response?.data || createProviderErr,\n 'error'\n );\n throw new Error(`Error creating provider ${entityId}`);\n }\n );\n } else {\n await updateProvider(entityLocation, providerData).catch(\n (updateProviderErr) => {\n printMessage(\n updateProviderErr.response?.data || updateProviderErr,\n 'error'\n );\n throw new Error(`Error updating provider ${entityId}`);\n }\n );\n }\n }\n }\n\n // Process circles of trust\n if (\n deps &&\n treeObject.circlesOfTrust &&\n Object.entries(treeObject.circlesOfTrust).length > 0\n ) {\n if (verbose) printMessage(' - SAML2 circles of trust:');\n for (const [cotId, cotData] of Object.entries(treeObject.circlesOfTrust)) {\n delete cotData['_rev'];\n if (verbose) printMessage(` - ${cotId}`, 'info');\n try {\n await createCircleOfTrust(cotData);\n } catch (createCotErr) {\n if (\n createCotErr.response?.status === 409 ||\n createCotErr.response?.status === 500\n ) {\n try {\n await updateCircleOfTrust(cotId, cotData);\n } catch (updateCotErr) {\n printMessage(createCotErr.response?.data || createCotErr, 'error');\n printMessage(updateCotErr.response?.data || updateCotErr, 'error');\n throw new Error(`Error creating/updating circle of trust ${cotId}`);\n }\n } else {\n printMessage(createCotErr.response?.data || createCotErr, 'error');\n throw new Error(`Error creating circle of trust ${cotId}`);\n }\n }\n }\n }\n\n // Process inner nodes\n let innerNodes = {};\n if (\n treeObject.innerNodes &&\n Object.entries(treeObject.innerNodes).length > 0\n ) {\n innerNodes = treeObject.innerNodes;\n }\n // old export file format\n else if (\n treeObject.innernodes &&\n Object.entries(treeObject.innernodes).length > 0\n ) {\n innerNodes = treeObject.innernodes;\n }\n if (Object.entries(innerNodes).length > 0) {\n if (verbose) printMessage(' - Inner nodes:', 'text', true);\n for (const [innerNodeId, innerNodeData] of Object.entries(innerNodes)) {\n delete innerNodeData['_rev'];\n const nodeType = innerNodeData['_type']['_id'];\n if (!reUuid) {\n newUuid = innerNodeId;\n } else {\n newUuid = uuidv4();\n uuidMap[innerNodeId] = newUuid;\n }\n innerNodeData['_id'] = newUuid;\n\n if (verbose)\n printMessage(\n ` - ${newUuid}${reUuid ? '*' : ''} (${nodeType})`,\n 'info',\n false\n );\n\n // If the node has an identityResource config setting\n // and the identityResource ends in 'user'\n // and the node's identityResource is the same as the tree's identityResource\n // change it to the current realm managed user identityResource otherwise leave it alone.\n if (\n innerNodeData['identityResource'] &&\n innerNodeData['identityResource'].endsWith('user') &&\n innerNodeData['identityResource'] === treeObject.tree.identityResource\n ) {\n innerNodeData['identityResource'] = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n `\\n - identityResource: ${innerNodeData['identityResource']}`,\n 'info',\n false\n );\n }\n try {\n await putNode(newUuid, nodeType, innerNodeData);\n } catch (nodeImportError) {\n if (\n nodeImportError.response.status === 400 &&\n nodeImportError.response.data.message ===\n 'Data validation failed for the attribute, Script'\n ) {\n throw new Error(\n `Missing script ${\n innerNodeData['script']\n } referenced by inner node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } (${innerNodeData['_type']['_id']}) in journey ${treeId}.`\n );\n } else if (\n nodeImportError.response?.status === 400 &&\n nodeImportError.response?.data?.message ===\n 'Invalid attribute specified.'\n ) {\n const { validAttributes } = nodeImportError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(innerNodeData)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete innerNodeData[attribute];\n }\n }\n try {\n await putNode(newUuid, nodeType, innerNodeData);\n } catch (nodeImportError2) {\n printMessage(nodeImportError2.response.data, 'error');\n throw new Error(\n `Error importing node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n } else {\n printMessage(nodeImportError.response.data, 'error');\n throw new Error(\n `Error importing inner node ${innerNodeId}${\n innerNodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process nodes\n if (treeObject.nodes && Object.entries(treeObject.nodes).length > 0) {\n if (verbose) printMessage(' - Nodes:');\n // eslint-disable-next-line prefer-const\n for (let [nodeId, nodeData] of Object.entries(treeObject.nodes)) {\n delete nodeData['_rev'];\n const nodeType = nodeData['_type']['_id'];\n if (!reUuid) {\n newUuid = nodeId;\n } else {\n newUuid = uuidv4();\n uuidMap[nodeId] = newUuid;\n }\n nodeData['_id'] = newUuid;\n\n if (nodeType === 'PageNode' && reUuid) {\n for (const [, inPageNodeData] of Object.entries(nodeData['nodes'])) {\n const currentId = inPageNodeData['_id'];\n nodeData = JSON.parse(\n replaceAll(JSON.stringify(nodeData), currentId, uuidMap[currentId])\n );\n }\n }\n\n if (verbose)\n printMessage(\n ` - ${newUuid}${reUuid ? '*' : ''} (${nodeType})`,\n 'info',\n false\n );\n\n // If the node has an identityResource config setting\n // and the identityResource ends in 'user'\n // and the node's identityResource is the same as the tree's identityResource\n // change it to the current realm managed user identityResource otherwise leave it alone.\n if (\n nodeData.identityResource &&\n nodeData.identityResource.endsWith('user') &&\n nodeData.identityResource === treeObject.tree.identityResource\n ) {\n nodeData['identityResource'] = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n `\\n - identityResource: ${nodeData['identityResource']}`,\n 'info',\n false\n );\n }\n try {\n await putNode(newUuid, nodeType, nodeData);\n } catch (nodeImportError) {\n if (\n nodeImportError.response.status === 400 &&\n nodeImportError.response.data.message ===\n 'Data validation failed for the attribute, Script'\n ) {\n throw new Error(\n `Missing script ${nodeData['script']} referenced by node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } (${nodeData['_type']['_id']}) in journey ${treeId}.`\n );\n } else if (\n nodeImportError.response?.status === 400 &&\n nodeImportError.response?.data?.message ===\n 'Invalid attribute specified.'\n ) {\n const { validAttributes } = nodeImportError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(nodeData)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete nodeData[attribute];\n }\n }\n try {\n await putNode(newUuid, nodeType, nodeData);\n } catch (nodeImportError2) {\n printMessage(nodeImportError2.response.data, 'error');\n throw new Error(\n `Error importing node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n } else {\n printMessage(nodeImportError.response.data, 'error');\n throw new Error(\n `Error importing node ${nodeId}${\n nodeId === newUuid ? '' : ` [${newUuid}]`\n } in journey ${treeId}`\n );\n }\n }\n if (verbose) printMessage('');\n }\n }\n\n // Process tree\n if (verbose) printMessage(' - Flow');\n\n if (reUuid) {\n let journeyText = JSON.stringify(treeObject.tree, null, 2);\n for (const [oldId, newId] of Object.entries(uuidMap)) {\n journeyText = replaceAll(journeyText, oldId, newId);\n }\n treeObject.tree = JSON.parse(journeyText);\n }\n\n // If the tree has an identityResource config setting\n // and the identityResource ends in 'user'\n // Set the identityResource for the tree to the selected resource.\n if (\n (treeObject.tree.identityResource &&\n (treeObject.tree['identityResource'] as string).endsWith('user')) ||\n state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY ||\n state.getDeploymentType() === globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY\n ) {\n treeObject.tree.identityResource = `managed/${getRealmManagedUser()}`;\n if (verbose)\n printMessage(\n ` - identityResource: ${treeObject.tree.identityResource}`,\n 'info',\n false\n );\n }\n\n delete treeObject.tree._rev;\n try {\n await putTree(treeObject.tree._id as string, treeObject.tree);\n if (verbose) printMessage(`\\n - Done`, 'info', true);\n } catch (importError) {\n if (\n importError.response?.status === 400 &&\n importError.response?.data?.message === 'Invalid attribute specified.'\n ) {\n const { validAttributes } = importError.response.data.detail;\n validAttributes.push('_id');\n for (const attribute of Object.keys(treeObject.tree)) {\n if (!validAttributes.includes(attribute)) {\n if (verbose)\n printMessage(\n `\\n - Removing invalid attribute: ${attribute}`,\n 'warn',\n false\n );\n delete treeObject.tree[attribute];\n }\n }\n try {\n await putTree(treeObject.tree._id as string, treeObject.tree);\n if (verbose) printMessage(`\\n - Done`, 'info', true);\n } catch (importError2) {\n printMessage(importError2.response.data, 'error');\n throw new Error(`Error importing journey flow ${treeId}`);\n }\n } else {\n printMessage(importError.response?.data || importError, 'error');\n debugMessage(importError.response?.data || importError);\n throw new Error(`\\nError importing journey flow ${treeId}`);\n }\n }\n}\n\n/**\n * Resolve journey dependencies\n * @param {Map} installedJorneys Map of installed journeys\n * @param {Map} journeyMap Map of journeys to resolve dependencies for\n * @param {[String]} unresolvedJourneys Map to hold the names of unresolved journeys and their dependencies\n * @param {[String]} resolvedJourneys Array to hold the names of resolved journeys\n * @param {int} index Depth of recursion\n */\nexport async function resolveDependencies(\n installedJorneys,\n journeyMap,\n unresolvedJourneys,\n resolvedJourneys,\n index = -1\n) {\n let before = -1;\n let after = index;\n if (index !== -1) {\n before = index;\n }\n\n for (const tree in journeyMap) {\n if ({}.hasOwnProperty.call(journeyMap, tree)) {\n const dependencies = [];\n for (const node in journeyMap[tree].nodes) {\n if (\n journeyMap[tree].nodes[node]._type._id === 'InnerTreeEvaluatorNode'\n ) {\n dependencies.push(journeyMap[tree].nodes[node].tree);\n }\n }\n let allResolved = true;\n for (const dependency of dependencies) {\n if (\n !resolvedJourneys.includes(dependency) &&\n !installedJorneys.includes(dependency)\n ) {\n allResolved = false;\n }\n }\n if (allResolved) {\n if (resolvedJourneys.indexOf(tree) === -1) resolvedJourneys.push(tree);\n // remove from unresolvedJourneys array\n // for (let i = 0; i < unresolvedJourneys.length; i += 1) {\n // if (unresolvedJourneys[i] === tree) {\n // unresolvedJourneys.splice(i, 1);\n // i -= 1;\n // }\n // }\n delete unresolvedJourneys[tree];\n // } else if (!unresolvedJourneys.includes(tree)) {\n } else {\n // unresolvedJourneys.push(tree);\n unresolvedJourneys[tree] = dependencies;\n }\n }\n }\n after = Object.keys(unresolvedJourneys).length;\n if (index !== -1 && after === before) {\n // This is the end, no progress was made since the last recursion\n // printMessage(\n // `Journeys with unresolved dependencies: ${unresolvedJourneys}`,\n // 'error'\n // );\n } else if (after > 0) {\n resolveDependencies(\n installedJorneys,\n journeyMap,\n unresolvedJourneys,\n resolvedJourneys,\n after\n );\n }\n}\n\n/**\n * Helper to import multiple trees from a tree map\n * @param {Object} treesMap map of trees object\n * @param {TreeImportOptions} options import options\n */\nexport async function importAllJourneys(\n treesMap: MultiTreeExportInterface,\n options: TreeImportOptions\n) {\n const installedJourneys = (await getTrees()).result.map((x) => x._id);\n const unresolvedJourneys = {};\n const resolvedJourneys = [];\n createProgressIndicator(undefined, 'Resolving dependencies', 'indeterminate');\n await resolveDependencies(\n installedJourneys,\n treesMap,\n unresolvedJourneys,\n resolvedJourneys\n );\n if (Object.keys(unresolvedJourneys).length === 0) {\n stopProgressIndicator(`Resolved all dependencies.`, 'success');\n } else {\n stopProgressIndicator(\n `${\n Object.keys(unresolvedJourneys).length\n } journeys with unresolved dependencies:`,\n 'fail'\n );\n for (const journey of Object.keys(unresolvedJourneys)) {\n printMessage(\n ` - ${journey} requires ${unresolvedJourneys[journey]}`,\n 'info'\n );\n }\n }\n createProgressIndicator(resolvedJourneys.length, 'Importing');\n for (const tree of resolvedJourneys) {\n try {\n // eslint-disable-next-line no-await-in-loop\n await importJourney(treesMap[tree], options);\n updateProgressIndicator(`${tree}`);\n } catch (error) {\n printMessage(`\\n${error.message}`, 'error');\n }\n }\n stopProgressIndicator('Done');\n}\n\n/**\n * Get the node reference obbject for a node object. Node reference objects\n * are used in a tree flow definition and within page nodes to reference\n * nodes. Among other things, node references contain all the non-configuration\n * meta data that exists for readaility, like the x/y coordinates of the node\n * and the display name chosen by the tree designer. The dislay name is the\n * only intuitive link between the graphical representation of the tree and\n * the node configurations that make up the tree.\n * @param nodeObj node object to retrieve the node reference object for\n * @param singleTreeExport tree export with or without dependencies\n * @returns {NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface} node reference object\n */\nexport function getNodeRef(\n nodeObj: NodeSkeleton,\n singleTreeExport: SingleTreeExportInterface\n): NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface {\n if (singleTreeExport.tree.nodes[nodeObj._id]) {\n return singleTreeExport.tree.nodes[nodeObj._id];\n } else {\n for (const node of Object.values(singleTreeExport.nodes)) {\n if (containerNodes.includes(node._type._id)) {\n for (const nodeRef of node.nodes) {\n if (nodeRef._id === nodeObj._id) {\n return nodeRef;\n }\n }\n }\n }\n }\n return undefined;\n}\n\n/**\n * Default tree export resolver used to resolve a tree id/name to a full export\n * w/o dependencies of that tree from a platform instance.\n * @param {string} treeId id/name of the tree to resolve\n * @returns {TreeExportResolverInterface} tree export\n */\nexport const onlineTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`onlineTreeExportResolver(${treeId})`);\n return await exportJourney(treeId, {\n deps: false,\n useStringArrays: false,\n });\n };\n\n/**\n * Tree export resolver used to resolve a tree id/name to a full export\n * of that tree from individual `treename.journey.json` export files.\n * @param {string} treeId id/name of the tree to resolve\n * @returns {TreeExportResolverInterface} tree export\n */\nexport const fileByIdTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`fileByIdTreeExportResolver(${treeId})`);\n let treeExport = createSingleTreeExportTemplate();\n const files = findFilesByName(getTypedFilename(`${treeId}`, 'journey'));\n try {\n const file = files.pop();\n const jsonData = JSON.parse(fs.readFileSync(file, 'utf8'));\n debugMessage(\n `fileByIdTreeExportResolver: resolved '${treeId}' to ${file}`\n );\n // did we resolve the tree we were asked to resolved?\n if (jsonData.tree?._id === treeId) {\n treeExport = jsonData;\n }\n // check if this is a file with multiple trees and get journey by id\n else if (jsonData.trees && jsonData.trees[treeId]) {\n treeExport = jsonData.trees[treeId];\n }\n } catch (error) {\n debugMessage(\n `fileByIdTreeExportResolver: unable to resolve '${treeId}' to a file.`\n );\n }\n return treeExport;\n };\n\n/**\n * Factory that creates a tree export resolver used to resolve a tree id\n * to a full export of that tree from a multi-tree export file.\n * @param {string} file multi-tree export file\n * @returns {TreeExportResolverInterface} tree export resolver\n */\nexport function createFileParamTreeExportResolver(\n file: string\n): TreeExportResolverInterface {\n const fileParamTreeExportResolver: TreeExportResolverInterface =\n async function (treeId: string) {\n debugMessage(`fileParamTreeExportResolver(${treeId})`);\n let treeExport: SingleTreeExportInterface =\n createSingleTreeExportTemplate();\n try {\n const jsonData = JSON.parse(fs.readFileSync(file, 'utf8'));\n // did we resolve the tree we were asked to resolved?\n if (jsonData.tree?._id === treeId) {\n treeExport = jsonData;\n }\n // check if this is a file with multiple trees and get journey by id\n else if (jsonData.trees && jsonData.trees[treeId]) {\n treeExport = jsonData.trees[treeId];\n }\n // fall back to fileByIdTreeExportResolver\n else {\n treeExport = await fileByIdTreeExportResolver(treeId);\n }\n } catch (error) {\n //\n }\n return treeExport;\n };\n debugMessage(`fileParamTreeExportResolver: file=${file}`);\n return fileParamTreeExportResolver;\n}\n\n/**\n * Get tree dependencies (all descendent inner trees)\n * @param {SingleTreeExportInterface} treeExport single tree export\n * @param {string[]} resolvedTreeIds list of tree ids wich have already been resolved\n * @param {TreeExportResolverInterface} resolveTreeExport tree export resolver callback function\n * @returns {Promise<TreeDependencyMapInterface>} a promise that resolves to a tree dependency map\n */\nexport async function getTreeDescendents(\n treeExport: SingleTreeExportInterface,\n resolveTreeExport: TreeExportResolverInterface = onlineTreeExportResolver,\n resolvedTreeIds: string[] = []\n): Promise<TreeDependencyMapInterface> {\n debugMessage(\n `getTreeDependencies(${treeExport.tree._id}, [${resolvedTreeIds.join(\n ', '\n )}])`\n );\n if (!resolvedTreeIds.includes(treeExport.tree._id)) {\n resolvedTreeIds.push(treeExport.tree._id);\n }\n const treeDependencyMap: TreeDependencyMapInterface = {\n [treeExport.tree._id]: [],\n };\n const dependencies: TreeDependencyMapInterface[] = [];\n for (const [nodeId, node] of Object.entries(treeExport.tree.nodes)) {\n const innerTreeId = treeExport.nodes[nodeId].tree;\n if (\n node.nodeType === 'InnerTreeEvaluatorNode' &&\n !resolvedTreeIds.includes(innerTreeId)\n ) {\n const innerTreeExport = await resolveTreeExport(innerTreeId);\n debugMessage(`resolved inner tree: ${innerTreeExport.tree._id}`);\n // resolvedTreeIds.push(innerTreeId);\n dependencies.push(\n await getTreeDescendents(\n innerTreeExport,\n resolveTreeExport,\n resolvedTreeIds\n )\n );\n }\n }\n treeDependencyMap[treeExport.tree._id] = dependencies;\n return treeDependencyMap;\n}\n\n/**\n * Find all node configuration objects that are no longer referenced by any tree\n * @returns {Promise<unknown[]>} a promise that resolves to an array of orphaned nodes\n */\nexport async function findOrphanedNodes(): Promise<unknown[]> {\n const allNodes = [];\n const orphanedNodes = [];\n let types = [];\n const allJourneys = (await getTrees()).result;\n let errorMessage = '';\n const errorTypes = [];\n\n createProgressIndicator(\n undefined,\n `Counting total nodes...`,\n 'indeterminate'\n );\n try {\n types = (await getNodeTypes()).result;\n } catch (error) {\n printMessage('Error retrieving all available node types:', 'error');\n printMessage(error.response.data, 'error');\n return [];\n }\n for (const type of types) {\n try {\n // eslint-disable-next-line no-await-in-loop, no-loop-func\n const nodes = (await getNodesByType(type._id)).result;\n for (const node of nodes) {\n allNodes.push(node);\n updateProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`\n );\n }\n } catch (error) {\n errorTypes.push(type._id);\n errorMessage = ` (Skipped type(s): ${errorTypes})`['yellow'];\n updateProgressIndicator(`${allNodes.length} total nodes${errorMessage}`);\n }\n }\n if (errorTypes.length > 0) {\n stopProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`,\n 'warn'\n );\n } else {\n stopProgressIndicator(`${allNodes.length} total nodes`, 'success');\n }\n\n createProgressIndicator(\n undefined,\n 'Counting active nodes...',\n 'indeterminate'\n );\n const activeNodes = [];\n for (const journey of allJourneys) {\n for (const nodeId in journey.nodes) {\n if ({}.hasOwnProperty.call(journey.nodes, nodeId)) {\n activeNodes.push(nodeId);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n const node = journey.nodes[nodeId];\n if (containerNodes.includes(node.nodeType)) {\n const containerNode = await getNode(nodeId, node.nodeType);\n for (const innerNode of containerNode.nodes) {\n activeNodes.push(innerNode._id);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n }\n }\n }\n }\n }\n stopProgressIndicator(`${activeNodes.length} active nodes`, 'success');\n\n createProgressIndicator(\n undefined,\n 'Calculating orphaned nodes...',\n 'indeterminate'\n );\n const diff = allNodes.filter((x) => !activeNodes.includes(x._id));\n for (const orphanedNode of diff) {\n orphanedNodes.push(orphanedNode);\n }\n stopProgressIndicator(`${orphanedNodes.length} orphaned nodes`, 'success');\n return orphanedNodes;\n}\n\n/**\n * Remove orphaned nodes\n * @param {[Object]} orphanedNodes Pass in an array of orphaned node configuration objects to remove\n * @returns {Promise<unknown[]>} a promise that resolves to an array nodes that encountered errors deleting\n */\nexport async function removeOrphanedNodes(\n orphanedNodes: unknown[]\n): Promise<unknown[]> {\n const errorNodes = [];\n createProgressIndicator(orphanedNodes.length, 'Removing orphaned nodes...');\n for (const node of orphanedNodes) {\n updateProgressIndicator(`Removing ${node['_id']}...`);\n try {\n // eslint-disable-next-line no-await-in-loop\n await deleteNode(node['_id'], node['_type']['_id']);\n } catch (deleteError) {\n errorNodes.push(node);\n printMessage(` ${deleteError}`, 'error');\n }\n }\n stopProgressIndicator(`Removed ${orphanedNodes.length} orphaned nodes.`);\n return errorNodes;\n}\n\n/**\n * Analyze if a journey contains any custom nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.\n */\nexport function isCustomJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isCustomNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Analyze if a journey contains any premium nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.\n */\nexport function isPremiumJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isPremiumNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Analyze if a journey contains any cloud-only nodes considering the detected or the overridden version.\n * @param {SingleTreeExportInterface} journey Journey/tree configuration object\n * @returns {boolean} True if the journey/tree contains any cloud-only nodes, false otherwise.\n */\nexport function isCloudOnlyJourney(journey: SingleTreeExportInterface) {\n const nodeList = Object.values(journey.nodes).concat(\n Object.values(journey.innerNodes)\n );\n for (const node of nodeList) {\n if (isCloudOnlyNode(node['_type']['_id'])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Get a journey's classifications, which can be one or multiple of:\n * - standard: can run on any instance of a ForgeRock platform\n * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud\n * - premium: utilizes nodes, which come at a premium\n * - custom: utilizes nodes not included in the ForgeRock platform release\n * @param {SingleTreeExportInterface} journey journey export data\n * @returns {JourneyClassification[]} an array of one or multiple classifications\n */\nexport function getJourneyClassification(\n journey: SingleTreeExportInterface\n): JourneyClassification[] {\n const classifications: JourneyClassification[] = [];\n const premium = isPremiumJourney(journey);\n const custom = isCustomJourney(journey);\n const cloud = isCloudOnlyJourney(journey);\n if (custom) {\n classifications.push(JourneyClassification.CUSTOM);\n } else if (cloud) {\n classifications.push(JourneyClassification.CLOUD);\n } else {\n classifications.push(JourneyClassification.STANDARD);\n }\n if (premium) classifications.push(JourneyClassification.PREMIUM);\n return classifications;\n}\n\n/**\n * Delete a journey\n * @param {string} journeyId journey id/name\n * @param {Object} options deep=true also delete all the nodes and inner nodes, verbose=true print verbose info\n */\nexport async function deleteJourney(\n journeyId: string,\n options,\n progress = true\n) {\n const { deep } = options;\n const { verbose } = options;\n const status = { nodes: {} };\n if (progress)\n createProgressIndicator(\n undefined,\n `Deleting ${journeyId}...`,\n 'indeterminate'\n );\n if (progress && verbose) stopProgressIndicator();\n return deleteTree(journeyId)\n .then(async (deleteTreeResponse) => {\n status['status'] = 'success';\n const nodePromises = [];\n if (verbose) printMessage(`Deleted ${journeyId} (tree)`, 'info');\n if (deep) {\n for (const [nodeId, nodeObject] of Object.entries(\n deleteTreeResponse.nodes\n )) {\n // delete inner nodes (nodes inside container nodes)\n if (containerNodes.includes(nodeObject['nodeType'])) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const containerNode = await getNode(\n nodeId,\n nodeObject['nodeType']\n );\n if (verbose)\n printMessage(\n `Read ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}`,\n 'info'\n );\n for (const innerNodeObject of containerNode.nodes) {\n nodePromises.push(\n deleteNode(innerNodeObject._id, innerNodeObject.nodeType)\n .then((response2) => {\n status.nodes[innerNodeObject._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${innerNodeObject._id} (${innerNodeObject.nodeType}) from ${journeyId}`,\n 'info'\n );\n return response2;\n })\n .catch((error) => {\n status.nodes[innerNodeObject._id] = {\n status: 'error',\n error,\n };\n if (verbose)\n printMessage(\n `Error deleting inner node ${innerNodeObject._id} (${innerNodeObject.nodeType}) from ${journeyId}: ${error}`,\n 'error'\n );\n })\n );\n }\n // finally delete the container node\n nodePromises.push(\n deleteNode(containerNode._id, containerNode['_type']['_id'])\n .then((response2) => {\n status.nodes[containerNode._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}`,\n 'info'\n );\n return response2;\n })\n .catch((error) => {\n if (\n error?.response?.data?.code === 500 &&\n error.response.data.message ===\n 'Unable to read SMS config: Node did not exist'\n ) {\n status.nodes[containerNode._id] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}`,\n 'info'\n );\n } else {\n status.nodes[containerNode._id] = {\n status: 'error',\n error,\n };\n if (verbose)\n printMessage(\n `Error deleting container node ${containerNode._id} (${containerNode['_type']['_id']}) from ${journeyId}: ${error.response.data.message}`,\n 'error'\n );\n }\n })\n );\n } catch (error) {\n if (verbose)\n printMessage(\n `Error getting container node ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}: ${error}`,\n 'error'\n );\n }\n } else {\n // delete the node\n nodePromises.push(\n deleteNode(nodeId, nodeObject['nodeType'])\n .then((response) => {\n status.nodes[nodeId] = { status: 'success' };\n if (verbose)\n printMessage(\n `Deleted ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}`,\n 'info'\n );\n return response;\n })\n .catch((error) => {\n status.nodes[nodeId] = { status: 'error', error };\n if (verbose)\n printMessage(\n `Error deleting node ${nodeId} (${nodeObject['nodeType']}) from ${journeyId}: ${error}`,\n 'error'\n );\n })\n );\n }\n }\n }\n // wait until all the node calls are complete\n await Promise.allSettled(nodePromises);\n\n // report status\n if (progress) {\n let nodeCount = 0;\n let errorCount = 0;\n for (const node of Object.keys(status.nodes)) {\n nodeCount += 1;\n if (status.nodes[node].status === 'error') errorCount += 1;\n }\n if (errorCount === 0) {\n stopProgressIndicator(\n `Deleted ${journeyId} and ${\n nodeCount - errorCount\n }/${nodeCount} nodes.`,\n 'success'\n );\n } else {\n stopProgressIndicator(\n `Deleted ${journeyId} and ${\n nodeCount - errorCount\n }/${nodeCount} nodes.`,\n 'fail'\n );\n }\n }\n return status;\n })\n .catch((error) => {\n status['status'] = 'error';\n status['error'] = error;\n stopProgressIndicator(`Error deleting ${journeyId}.`, 'fail');\n if (verbose)\n printMessage(`Error deleting tree ${journeyId}: ${error}`, 'error');\n return status;\n });\n}\n\n/**\n * Delete all journeys\n * @param {Object} options deep=true also delete all the nodes and inner nodes, verbose=true print verbose info\n */\nexport async function deleteJourneys(options) {\n const { verbose } = options;\n const status = {};\n const trees = (await getTrees()).result;\n createProgressIndicator(trees.length, 'Deleting journeys...');\n for (const tree of trees) {\n if (verbose) printMessage('');\n // eslint-disable-next-line no-await-in-loop\n status[tree._id] = await deleteJourney(tree._id, options, false);\n updateProgressIndicator(`${tree._id}`);\n // introduce a 100ms wait to allow the progress bar to update before the next verbose message prints from the async function\n if (verbose)\n // eslint-disable-next-line no-await-in-loop\n await new Promise((r) => {\n setTimeout(r, 100);\n });\n }\n let journeyCount = 0;\n let journeyErrorCount = 0;\n let nodeCount = 0;\n let nodeErrorCount = 0;\n for (const journey of Object.keys(status)) {\n journeyCount += 1;\n if (status[journey].status === 'error') journeyErrorCount += 1;\n for (const node of Object.keys(status[journey].nodes)) {\n nodeCount += 1;\n if (status[journey].nodes[node].status === 'error') nodeErrorCount += 1;\n }\n }\n stopProgressIndicator(\n `Deleted ${journeyCount - journeyErrorCount}/${journeyCount} journeys and ${\n nodeCount - nodeErrorCount\n }/${nodeCount} nodes.`\n );\n return status;\n}\n\n/**\n * Enable a journey\n * @param journeyId journey id/name\n * @returns {Promise<boolean>} true if the operation was successful, false otherwise\n */\nexport async function enableJourney(journeyId: string): Promise<boolean> {\n try {\n const treeObject = await getTree(journeyId);\n treeObject['enabled'] = true;\n delete treeObject._rev;\n const newTreeObject = await putTree(journeyId, treeObject);\n return newTreeObject['enabled'] === true;\n } catch (error) {\n printMessage(error.response.data, 'error');\n return false;\n }\n}\n\n/**\n * Disable a journey\n * @param journeyId journey id/name\n * @returns {Promise<boolean>} true if the operation was successful, false otherwise\n */\nexport async function disableJourney(journeyId: string): Promise<boolean> {\n try {\n const treeObject = await getTree(journeyId);\n treeObject['enabled'] = false;\n delete treeObject._rev;\n const newTreeObject = await putTree(journeyId, treeObject);\n return newTreeObject['enabled'] === false;\n } catch (error) {\n printMessage(error.response.data, 'error');\n return false;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/LogOps.ts"],"names":[],"mappings":"AAwOA,wBAAgB,kBAAkB,aAEjC;AAED,wBAAgB,YAAY,CAAC,KAAK,KAAA,OAOjC;AAID,wBAAgB,mBAAmB,CAAC,GAAG,KAAA,OAStC;AAED,wBAAsB,aAAa,mBAgBlC;AAED,wBAAsB,QAAQ,CAAC,MAAM,KAAA,EAAE,MAAM,KAAA,EAAE,IAAI,KAAA,EAAE,MAAM,KAAA,EAAE,EAAE,KAAA,mBAwC9D;AAED,wBAAsB,cAAc,iBA2CnC;AAED,wBAAsB,SAAS,CAC7B,MAAM,KAAA,EACN,OAAO,KAAA,EACP,KAAK,KAAA,EACL,MAAM,KAAA,EACN,IAAI,KAAA,EACJ,QAAQ,KAAA,EACR,MAAM,KAAA,EACN,EAAE,KAAA,mBAsDH","file":"LogOps.d.ts","sourcesContent":["import { printMessage } from './utils/Console';\nimport { getCurrentTimestamp } from './utils/ExportImportUtils';\nimport { createAPIKeyAndSecret, getAPIKeys, getSources } from '../api/LogApi';\n\nimport storage from '../storage/SessionStorage';\n\nimport * as LogApi from '../api/LogApi';\n\n// eslint-disable-next-line no-unused-vars\nconst unfilterableNoise = [\n 'text/plain', // Unfortunately, it is impossible to filter out those without excluding IDM script logging as well\n];\n\nconst miscNoise = [\n 'com.iplanet.dpro.session.operations.ServerSessionOperationStrategy',\n 'com.iplanet.dpro.session.SessionIDFactory',\n 'com.iplanet.dpro.session.share.SessionEncodeURL',\n 'com.iplanet.services.naming.WebtopNaming',\n 'com.iplanet.sso.providers.dpro.SSOProviderImpl',\n 'com.sun.identity.authentication.AuthContext',\n 'com.sun.identity.authentication.client.AuthClientUtils',\n 'com.sun.identity.authentication.config.AMAuthConfigType',\n 'com.sun.identity.authentication.config.AMAuthenticationManager',\n 'com.sun.identity.authentication.config.AMAuthLevelManager',\n 'com.sun.identity.authentication.config.AMConfiguration',\n 'com.sun.identity.authentication.jaas.LoginContext',\n 'com.sun.identity.authentication.modules.application.Application',\n 'com.sun.identity.authentication.server.AuthContextLocal',\n 'com.sun.identity.authentication.service.AMLoginContext',\n 'com.sun.identity.authentication.service.AuthContextLookup',\n 'com.sun.identity.authentication.service.AuthD',\n 'com.sun.identity.authentication.service.AuthUtils',\n 'com.sun.identity.authentication.service.DSAMECallbackHandler',\n 'com.sun.identity.authentication.service.LoginState',\n 'com.sun.identity.authentication.spi.AMLoginModule',\n 'com.sun.identity.delegation.DelegationEvaluatorImpl',\n 'com.sun.identity.idm.plugins.internal.AgentsRepo',\n 'com.sun.identity.idm.server.IdCachedServicesImpl',\n 'com.sun.identity.idm.server.IdRepoPluginsCache',\n 'com.sun.identity.idm.server.IdServicesImpl',\n 'com.sun.identity.log.spi.ISDebug',\n 'com.sun.identity.shared.encode.CookieUtils',\n 'com.sun.identity.sm.ldap.SMSLdapObject',\n 'com.sun.identity.sm.CachedSMSEntry',\n 'com.sun.identity.sm.CachedSubEntries',\n 'com.sun.identity.sm.DNMapper',\n 'com.sun.identity.sm.ServiceConfigImpl',\n 'com.sun.identity.sm.ServiceConfigManagerImpl',\n 'com.sun.identity.sm.SMSEntry',\n 'com.sun.identity.sm.SMSUtils',\n 'com.sun.identity.sm.SmsWrapperObject',\n 'oauth2',\n 'org.apache.http.client.protocol.RequestAuthCache',\n 'org.apache.http.impl.conn.PoolingHttpClientConnectionManager',\n 'org.apache.http.impl.nio.client.InternalHttpAsyncClient',\n 'org.apache.http.impl.nio.client.InternalIODispatch',\n 'org.apache.http.impl.nio.client.MainClientExec',\n 'org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl',\n 'org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager',\n 'org.forgerock.audit.AuditServiceImpl',\n 'org.forgerock.oauth2.core.RealmOAuth2ProviderSettings',\n 'org.forgerock.openam.authentication.service.JAASModuleDetector',\n 'org.forgerock.openam.authentication.service.LoginContextFactory',\n 'org.forgerock.openam.blacklist.BloomFilterBlacklist',\n 'org.forgerock.openam.blacklist.CTSBlacklist',\n 'org.forgerock.openam.core.realms.impl.CachingRealmLookup',\n 'org.forgerock.openam.core.rest.authn.RestAuthCallbackHandlerManager',\n 'org.forgerock.openam.core.rest.authn.trees.AuthTrees',\n 'org.forgerock.openam.cors.CorsFilter',\n 'org.forgerock.openam.cts.CTSPersistentStoreImpl',\n 'org.forgerock.openam.cts.impl.CoreTokenAdapter',\n 'org.forgerock.openam.cts.impl.queue.AsyncResultHandler',\n 'org.forgerock.openam.cts.reaper.ReaperDeleteOnQueryResultHandler',\n 'org.forgerock.openam.headers.DisableSameSiteCookiesFilter',\n 'org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo',\n 'org.forgerock.openam.rest.CsrfFilter',\n 'org.forgerock.openam.rest.restAuthenticationFilter',\n 'org.forgerock.openam.rest.fluent.CrestLoggingFilter',\n 'org.forgerock.openam.session.cts.CtsOperations',\n 'org.forgerock.openam.session.stateless.StatelessSessionManager',\n 'org.forgerock.openam.sm.datalayer.impl.ldap.ExternalLdapConfig',\n 'org.forgerock.openam.sm.datalayer.impl.ldap.LdapQueryBuilder',\n 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutor',\n 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutorThread',\n 'org.forgerock.openam.sm.datalayer.providers.LdapConnectionFactoryProvider',\n 'org.forgerock.openam.sm.file.ConfigFileSystemHandler',\n 'org.forgerock.openam.social.idp.SocialIdentityProviders',\n 'org.forgerock.openam.utils.ClientUtils',\n 'org.forgerock.opendj.ldap.CachedConnectionPool',\n 'org.forgerock.opendj.ldap.LoadBalancer',\n 'org.forgerock.secrets.keystore.KeyStoreSecretStore',\n 'org.forgerock.secrets.propertyresolver.PropertyResolverSecretStore',\n 'org.forgerock.secrets.SecretsProvider',\n];\n\nconst journeysNoise = [\n 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',\n];\n\n// eslint-disable-next-line no-unused-vars\nconst journeys = [\n 'org.forgerock.openam.auth.nodes.SelectIdPNode',\n 'org.forgerock.openam.auth.nodes.ValidatedPasswordNode',\n 'org.forgerock.openam.auth.nodes.ValidatedUsernameNode',\n 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',\n];\n\nconst samlNoise = [\n 'com.sun.identity.cot.COTCache',\n 'com.sun.identity.plugin.configuration.impl.ConfigurationInstanceImpl',\n 'com.sun.identity.saml2.meta.SAML2MetaCache',\n 'com.sun.identity.saml2.profile.CacheCleanUpRunnable',\n 'org.apache.xml.security.keys.KeyInfo',\n 'org.apache.xml.security.signature.XMLSignature',\n 'org.apache.xml.security.utils.SignerOutputStream',\n 'org.apache.xml.security.utils.resolver.ResourceResolver',\n 'org.apache.xml.security.utils.resolver.implementations.ResolverFragment',\n 'org.apache.xml.security.algorithms.JCEMapper',\n 'org.apache.xml.security.algorithms.implementations.SignatureBaseRSA',\n 'org.apache.xml.security.algorithms.SignatureAlgorithm',\n 'org.apache.xml.security.utils.ElementProxy',\n 'org.apache.xml.security.transforms.Transforms',\n 'org.apache.xml.security.utils.DigesterOutputStream',\n 'org.apache.xml.security.signature.Reference',\n 'org.apache.xml.security.signature.Manifest',\n];\n\n// eslint-disable-next-line no-unused-vars\nconst saml = [\n 'jsp.saml2.spAssertionConsumer',\n 'com.sun.identity.saml.common.SAMLUtils',\n 'com.sun.identity.saml2.common.SAML2Utils',\n 'com.sun.identity.saml2.meta.SAML2MetaManager',\n 'com.sun.identity.saml2.xmlsig.FMSigProvider',\n];\n\nconst noise = miscNoise.concat(samlNoise).concat(journeysNoise);\n\nconst numLogLevelMap = {\n 0: ['SEVERE', 'ERROR', 'FATAL'],\n 1: ['WARNING', 'WARN', 'CONFIG'],\n 2: ['INFO', 'INFORMATION'],\n 3: ['DEBUG', 'FINE', 'FINER', 'FINEST'],\n 4: ['ALL'],\n};\n\nconst logLevelMap = {\n SEVERE: ['SEVERE', 'ERROR', 'FATAL'],\n ERROR: ['SEVERE', 'ERROR', 'FATAL'],\n FATAL: ['SEVERE', 'ERROR', 'FATAL'],\n WARN: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n WARNING: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n CONFIG: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n INFO: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n ],\n INFORMATION: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n ],\n DEBUG: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINE: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINER: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINEST: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n ALL: ['ALL'],\n};\n\nexport function defaultNoiseFilter() {\n return noise;\n}\n\nexport function resolveLevel(level) {\n // const levels = ['FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL'];\n // levels.splice(levels.indexOf(levelName) + 1, levels.length);\n if (Number.isNaN(parseInt(level, 10))) {\n return logLevelMap[level];\n }\n return logLevelMap[numLogLevelMap[level][0]];\n}\n\n// It seems that the undesirable 'text/plain' logs start with a date, not a LEVEL\n// Therefore, for those, this function returns null, and thus filters out the undesirable\nexport function resolvePayloadLevel(log) {\n try {\n return log.type !== 'text/plain'\n ? log.payload.level\n : log.payload.match(/^([^:]*):/)[1];\n } catch (e) {\n // Fail-safe for no group match\n return null;\n }\n}\n\nexport async function getLogSources() {\n const sources = [];\n await getSources()\n .then((response) => {\n response.data.result.forEach((item) => {\n sources.push(item);\n });\n })\n .catch((error) => {\n printMessage(\n `getSources ERROR: get log sources call returned ${error}}`,\n 'error'\n );\n return [];\n });\n return sources;\n}\n\nexport async function tailLogs(source, levels, txid, cookie, nf) {\n try {\n const response = await LogApi.tail(source, cookie);\n if (response.status < 200 || response.status > 399) {\n printMessage(\n `tail ERROR: tail call returned ${response.status}`,\n 'error'\n );\n return null;\n }\n // if (!cookie) {\n // await saveConnection();\n // }\n const logsObject = response.data;\n let filteredLogs = [];\n const noiseFilter = nf == null ? noise : nf;\n if (Array.isArray(logsObject.result)) {\n filteredLogs = logsObject.result.filter(\n (el) =>\n !noiseFilter.includes(el.payload.logger) &&\n !noiseFilter.includes(el.type) &&\n (levels[0] === 'ALL' || levels.includes(resolvePayloadLevel(el))) &&\n (typeof txid === 'undefined' ||\n txid === null ||\n el.payload.transactionId?.includes(txid))\n );\n }\n\n filteredLogs.forEach((e) => {\n printMessage(JSON.stringify(e.payload), 'data');\n });\n\n setTimeout(() => {\n tailLogs(source, levels, txid, logsObject.result.pagedResultsCookie, nf);\n }, 5000);\n return null;\n } catch (e) {\n printMessage(`tail ERROR: tail data error - ${e}`, 'error');\n return `tail ERROR: tail data error - ${e}`;\n }\n}\n\nexport async function provisionCreds() {\n try {\n let keyName = `frodo-${storage.session.getUsername()}`;\n return getAPIKeys()\n .then((response) => {\n response.data.result.forEach((k) => {\n if (k.name === keyName) {\n // append current timestamp to name if the named key already exists\n keyName = `${keyName}-${getCurrentTimestamp()}`;\n }\n });\n return createAPIKeyAndSecret(keyName)\n .then((resp) => {\n if (resp.data.name !== keyName) {\n printMessage(\n `create keys ERROR: could not create log API key ${keyName}`,\n 'error'\n );\n return null;\n }\n printMessage(\n `Created a new log API key [${keyName}] in ${storage.session.getTenant()}`\n );\n return resp.data;\n })\n .catch((error) => {\n printMessage(\n `create keys ERROR: create keys call returned ${error}`,\n 'error'\n );\n return null;\n });\n })\n .catch((error) => {\n printMessage(\n `get keys ERROR: get keys call returned ${error}`,\n 'error'\n );\n });\n } catch (e) {\n printMessage(`create keys ERROR: create keys data error - ${e}`, 'error');\n return null;\n }\n}\n\nexport async function fetchLogs(\n source,\n startTs,\n endTs,\n levels,\n txid,\n ffString,\n cookie,\n nf\n) {\n try {\n // console.log(`startTs: ${startTs} endTs : ${endTs}`);\n const response = await LogApi.fetch(source, startTs, endTs, cookie);\n if (response.status < 200 || response.status > 399) {\n printMessage(\n `fetch ERROR: fetch call returned ${response.status}`,\n 'error'\n );\n return null;\n }\n const logsObject = response.data;\n let filteredLogs = [];\n const noiseFilter = nf == null ? noise : nf;\n if (Array.isArray(logsObject.result)) {\n filteredLogs = logsObject.result.filter(\n (el) =>\n !noiseFilter.includes(el.payload.logger) &&\n !noiseFilter.includes(el.type) &&\n (levels[0] === 'ALL' || levels.includes(resolvePayloadLevel(el))) &&\n (typeof txid === 'undefined' ||\n txid === null ||\n el.payload.transactionId?.includes(txid))\n );\n }\n\n filteredLogs.forEach((e) => {\n const log = JSON.stringify(e, null, 2);\n if (ffString) {\n if (log.includes(ffString)) {\n printMessage(log, 'data');\n }\n } else {\n printMessage(log, 'data');\n }\n });\n if (logsObject.pagedResultsCookie != null) {\n await fetchLogs(\n source,\n startTs,\n endTs,\n levels,\n txid,\n ffString,\n logsObject.pagedResultsCookie,\n nf\n );\n }\n return null;\n } catch (e) {\n printMessage(`fetch ERROR: fetch data error - ${e}`, 'error');\n return `fetch ERROR: fetch data error - ${e}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/LogOps.ts"],"names":[],"mappings":"AAwOA,wBAAgB,kBAAkB,aAEjC;AAED,wBAAgB,YAAY,CAAC,KAAK,KAAA,OAOjC;AAID,wBAAgB,mBAAmB,CAAC,GAAG,KAAA,OAStC;AAED,wBAAsB,aAAa,mBAgBlC;AAED,wBAAsB,QAAQ,CAAC,MAAM,KAAA,EAAE,MAAM,KAAA,EAAE,IAAI,KAAA,EAAE,MAAM,KAAA,EAAE,EAAE,KAAA,mBAwC9D;AAED,wBAAsB,cAAc,iBA2CnC;AAED,wBAAsB,SAAS,CAC7B,MAAM,KAAA,EACN,OAAO,KAAA,EACP,KAAK,KAAA,EACL,MAAM,KAAA,EACN,IAAI,KAAA,EACJ,QAAQ,KAAA,EACR,MAAM,KAAA,EACN,EAAE,KAAA,mBAsDH","file":"LogOps.d.ts","sourcesContent":["import { printMessage } from './utils/Console';\nimport { getCurrentTimestamp } from './utils/ExportImportUtils';\nimport { createAPIKeyAndSecret, getAPIKeys, getSources } from '../api/LogApi';\n\nimport * as state from '../shared/State';\n\nimport * as LogApi from '../api/LogApi';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst unfilterableNoise = [\n 'text/plain', // Unfortunately, it is impossible to filter out those without excluding IDM script logging as well\n];\n\nconst miscNoise = [\n 'com.iplanet.dpro.session.operations.ServerSessionOperationStrategy',\n 'com.iplanet.dpro.session.SessionIDFactory',\n 'com.iplanet.dpro.session.share.SessionEncodeURL',\n 'com.iplanet.services.naming.WebtopNaming',\n 'com.iplanet.sso.providers.dpro.SSOProviderImpl',\n 'com.sun.identity.authentication.AuthContext',\n 'com.sun.identity.authentication.client.AuthClientUtils',\n 'com.sun.identity.authentication.config.AMAuthConfigType',\n 'com.sun.identity.authentication.config.AMAuthenticationManager',\n 'com.sun.identity.authentication.config.AMAuthLevelManager',\n 'com.sun.identity.authentication.config.AMConfiguration',\n 'com.sun.identity.authentication.jaas.LoginContext',\n 'com.sun.identity.authentication.modules.application.Application',\n 'com.sun.identity.authentication.server.AuthContextLocal',\n 'com.sun.identity.authentication.service.AMLoginContext',\n 'com.sun.identity.authentication.service.AuthContextLookup',\n 'com.sun.identity.authentication.service.AuthD',\n 'com.sun.identity.authentication.service.AuthUtils',\n 'com.sun.identity.authentication.service.DSAMECallbackHandler',\n 'com.sun.identity.authentication.service.LoginState',\n 'com.sun.identity.authentication.spi.AMLoginModule',\n 'com.sun.identity.delegation.DelegationEvaluatorImpl',\n 'com.sun.identity.idm.plugins.internal.AgentsRepo',\n 'com.sun.identity.idm.server.IdCachedServicesImpl',\n 'com.sun.identity.idm.server.IdRepoPluginsCache',\n 'com.sun.identity.idm.server.IdServicesImpl',\n 'com.sun.identity.log.spi.ISDebug',\n 'com.sun.identity.shared.encode.CookieUtils',\n 'com.sun.identity.sm.ldap.SMSLdapObject',\n 'com.sun.identity.sm.CachedSMSEntry',\n 'com.sun.identity.sm.CachedSubEntries',\n 'com.sun.identity.sm.DNMapper',\n 'com.sun.identity.sm.ServiceConfigImpl',\n 'com.sun.identity.sm.ServiceConfigManagerImpl',\n 'com.sun.identity.sm.SMSEntry',\n 'com.sun.identity.sm.SMSUtils',\n 'com.sun.identity.sm.SmsWrapperObject',\n 'oauth2',\n 'org.apache.http.client.protocol.RequestAuthCache',\n 'org.apache.http.impl.conn.PoolingHttpClientConnectionManager',\n 'org.apache.http.impl.nio.client.InternalHttpAsyncClient',\n 'org.apache.http.impl.nio.client.InternalIODispatch',\n 'org.apache.http.impl.nio.client.MainClientExec',\n 'org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl',\n 'org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager',\n 'org.forgerock.audit.AuditServiceImpl',\n 'org.forgerock.oauth2.core.RealmOAuth2ProviderSettings',\n 'org.forgerock.openam.authentication.service.JAASModuleDetector',\n 'org.forgerock.openam.authentication.service.LoginContextFactory',\n 'org.forgerock.openam.blacklist.BloomFilterBlacklist',\n 'org.forgerock.openam.blacklist.CTSBlacklist',\n 'org.forgerock.openam.core.realms.impl.CachingRealmLookup',\n 'org.forgerock.openam.core.rest.authn.RestAuthCallbackHandlerManager',\n 'org.forgerock.openam.core.rest.authn.trees.AuthTrees',\n 'org.forgerock.openam.cors.CorsFilter',\n 'org.forgerock.openam.cts.CTSPersistentStoreImpl',\n 'org.forgerock.openam.cts.impl.CoreTokenAdapter',\n 'org.forgerock.openam.cts.impl.queue.AsyncResultHandler',\n 'org.forgerock.openam.cts.reaper.ReaperDeleteOnQueryResultHandler',\n 'org.forgerock.openam.headers.DisableSameSiteCookiesFilter',\n 'org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo',\n 'org.forgerock.openam.rest.CsrfFilter',\n 'org.forgerock.openam.rest.restAuthenticationFilter',\n 'org.forgerock.openam.rest.fluent.CrestLoggingFilter',\n 'org.forgerock.openam.session.cts.CtsOperations',\n 'org.forgerock.openam.session.stateless.StatelessSessionManager',\n 'org.forgerock.openam.sm.datalayer.impl.ldap.ExternalLdapConfig',\n 'org.forgerock.openam.sm.datalayer.impl.ldap.LdapQueryBuilder',\n 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutor',\n 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutorThread',\n 'org.forgerock.openam.sm.datalayer.providers.LdapConnectionFactoryProvider',\n 'org.forgerock.openam.sm.file.ConfigFileSystemHandler',\n 'org.forgerock.openam.social.idp.SocialIdentityProviders',\n 'org.forgerock.openam.utils.ClientUtils',\n 'org.forgerock.opendj.ldap.CachedConnectionPool',\n 'org.forgerock.opendj.ldap.LoadBalancer',\n 'org.forgerock.secrets.keystore.KeyStoreSecretStore',\n 'org.forgerock.secrets.propertyresolver.PropertyResolverSecretStore',\n 'org.forgerock.secrets.SecretsProvider',\n];\n\nconst journeysNoise = [\n 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',\n];\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst journeys = [\n 'org.forgerock.openam.auth.nodes.SelectIdPNode',\n 'org.forgerock.openam.auth.nodes.ValidatedPasswordNode',\n 'org.forgerock.openam.auth.nodes.ValidatedUsernameNode',\n 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',\n];\n\nconst samlNoise = [\n 'com.sun.identity.cot.COTCache',\n 'com.sun.identity.plugin.configuration.impl.ConfigurationInstanceImpl',\n 'com.sun.identity.saml2.meta.SAML2MetaCache',\n 'com.sun.identity.saml2.profile.CacheCleanUpRunnable',\n 'org.apache.xml.security.keys.KeyInfo',\n 'org.apache.xml.security.signature.XMLSignature',\n 'org.apache.xml.security.utils.SignerOutputStream',\n 'org.apache.xml.security.utils.resolver.ResourceResolver',\n 'org.apache.xml.security.utils.resolver.implementations.ResolverFragment',\n 'org.apache.xml.security.algorithms.JCEMapper',\n 'org.apache.xml.security.algorithms.implementations.SignatureBaseRSA',\n 'org.apache.xml.security.algorithms.SignatureAlgorithm',\n 'org.apache.xml.security.utils.ElementProxy',\n 'org.apache.xml.security.transforms.Transforms',\n 'org.apache.xml.security.utils.DigesterOutputStream',\n 'org.apache.xml.security.signature.Reference',\n 'org.apache.xml.security.signature.Manifest',\n];\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst saml = [\n 'jsp.saml2.spAssertionConsumer',\n 'com.sun.identity.saml.common.SAMLUtils',\n 'com.sun.identity.saml2.common.SAML2Utils',\n 'com.sun.identity.saml2.meta.SAML2MetaManager',\n 'com.sun.identity.saml2.xmlsig.FMSigProvider',\n];\n\nconst noise = miscNoise.concat(samlNoise).concat(journeysNoise);\n\nconst numLogLevelMap = {\n 0: ['SEVERE', 'ERROR', 'FATAL'],\n 1: ['WARNING', 'WARN', 'CONFIG'],\n 2: ['INFO', 'INFORMATION'],\n 3: ['DEBUG', 'FINE', 'FINER', 'FINEST'],\n 4: ['ALL'],\n};\n\nconst logLevelMap = {\n SEVERE: ['SEVERE', 'ERROR', 'FATAL'],\n ERROR: ['SEVERE', 'ERROR', 'FATAL'],\n FATAL: ['SEVERE', 'ERROR', 'FATAL'],\n WARN: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n WARNING: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n CONFIG: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],\n INFO: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n ],\n INFORMATION: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n ],\n DEBUG: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINE: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINER: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n FINEST: [\n 'SEVERE',\n 'ERROR',\n 'FATAL',\n 'WARNING',\n 'WARN',\n 'CONFIG',\n 'INFO',\n 'INFORMATION',\n 'DEBUG',\n 'FINE',\n 'FINER',\n 'FINEST',\n ],\n ALL: ['ALL'],\n};\n\nexport function defaultNoiseFilter() {\n return noise;\n}\n\nexport function resolveLevel(level) {\n // const levels = ['FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL'];\n // levels.splice(levels.indexOf(levelName) + 1, levels.length);\n if (Number.isNaN(parseInt(level, 10))) {\n return logLevelMap[level];\n }\n return logLevelMap[numLogLevelMap[level][0]];\n}\n\n// It seems that the undesirable 'text/plain' logs start with a date, not a LEVEL\n// Therefore, for those, this function returns null, and thus filters out the undesirable\nexport function resolvePayloadLevel(log) {\n try {\n return log.type !== 'text/plain'\n ? log.payload.level\n : log.payload.match(/^([^:]*):/)[1];\n } catch (e) {\n // Fail-safe for no group match\n return null;\n }\n}\n\nexport async function getLogSources() {\n const sources = [];\n await getSources()\n .then((response) => {\n response.data.result.forEach((item) => {\n sources.push(item);\n });\n })\n .catch((error) => {\n printMessage(\n `getSources ERROR: get log sources call returned ${error}}`,\n 'error'\n );\n return [];\n });\n return sources;\n}\n\nexport async function tailLogs(source, levels, txid, cookie, nf) {\n try {\n const response = await LogApi.tail(source, cookie);\n if (response.status < 200 || response.status > 399) {\n printMessage(\n `tail ERROR: tail call returned ${response.status}`,\n 'error'\n );\n return null;\n }\n // if (!cookie) {\n // await saveConnection();\n // }\n const logsObject = response.data;\n let filteredLogs = [];\n const noiseFilter = nf == null ? noise : nf;\n if (Array.isArray(logsObject.result)) {\n filteredLogs = logsObject.result.filter(\n (el) =>\n !noiseFilter.includes(el.payload.logger) &&\n !noiseFilter.includes(el.type) &&\n (levels[0] === 'ALL' || levels.includes(resolvePayloadLevel(el))) &&\n (typeof txid === 'undefined' ||\n txid === null ||\n el.payload.transactionId?.includes(txid))\n );\n }\n\n filteredLogs.forEach((e) => {\n printMessage(JSON.stringify(e.payload), 'data');\n });\n\n setTimeout(() => {\n tailLogs(source, levels, txid, logsObject.result.pagedResultsCookie, nf);\n }, 5000);\n return null;\n } catch (e) {\n printMessage(`tail ERROR: tail data error - ${e}`, 'error');\n return `tail ERROR: tail data error - ${e}`;\n }\n}\n\nexport async function provisionCreds() {\n try {\n let keyName = `frodo-${state.getUsername()}`;\n return getAPIKeys()\n .then((response) => {\n response.data.result.forEach((k) => {\n if (k.name === keyName) {\n // append current timestamp to name if the named key already exists\n keyName = `${keyName}-${getCurrentTimestamp()}`;\n }\n });\n return createAPIKeyAndSecret(keyName)\n .then((resp) => {\n if (resp.data.name !== keyName) {\n printMessage(\n `create keys ERROR: could not create log API key ${keyName}`,\n 'error'\n );\n return null;\n }\n printMessage(\n `Created a new log API key [${keyName}] in ${state.getHost()}`\n );\n return resp.data;\n })\n .catch((error) => {\n printMessage(\n `create keys ERROR: create keys call returned ${error}`,\n 'error'\n );\n return null;\n });\n })\n .catch((error) => {\n printMessage(\n `get keys ERROR: get keys call returned ${error}`,\n 'error'\n );\n });\n } catch (e) {\n printMessage(`create keys ERROR: create keys data error - ${e}`, 'error');\n return null;\n }\n}\n\nexport async function fetchLogs(\n source,\n startTs,\n endTs,\n levels,\n txid,\n ffString,\n cookie,\n nf\n) {\n try {\n // console.log(`startTs: ${startTs} endTs : ${endTs}`);\n const response = await LogApi.fetch(source, startTs, endTs, cookie);\n if (response.status < 200 || response.status > 399) {\n printMessage(\n `fetch ERROR: fetch call returned ${response.status}`,\n 'error'\n );\n return null;\n }\n const logsObject = response.data;\n let filteredLogs = [];\n const noiseFilter = nf == null ? noise : nf;\n if (Array.isArray(logsObject.result)) {\n filteredLogs = logsObject.result.filter(\n (el) =>\n !noiseFilter.includes(el.payload.logger) &&\n !noiseFilter.includes(el.type) &&\n (levels[0] === 'ALL' || levels.includes(resolvePayloadLevel(el))) &&\n (typeof txid === 'undefined' ||\n txid === null ||\n el.payload.transactionId?.includes(txid))\n );\n }\n\n filteredLogs.forEach((e) => {\n const log = JSON.stringify(e, null, 2);\n if (ffString) {\n if (log.includes(ffString)) {\n printMessage(log, 'data');\n }\n } else {\n printMessage(log, 'data');\n }\n });\n if (logsObject.pagedResultsCookie != null) {\n await fetchLogs(\n source,\n startTs,\n endTs,\n levels,\n txid,\n ffString,\n logsObject.pagedResultsCookie,\n nf\n );\n }\n return null;\n } catch (e) {\n printMessage(`fetch ERROR: fetch data error - ${e}`, 'error');\n return `fetch ERROR: fetch data error - ${e}`;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/NodeOps.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAoGhD;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAgF5D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,OAAO,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAepB;AAsOD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA6CtD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAc5E","file":"NodeOps.d.ts","sourcesContent":["import _ from 'lodash';\nimport {\n InnerNodeRefSkeletonInterface,\n NodeRefSkeletonInterface,\n NodeSkeleton,\n} from '../api/ApiTypes';\nimport storage from '../storage/SessionStorage';\nimport {\n getNode,\n deleteNode,\n getNodeTypes,\n getNodesByType,\n} from '../api/NodeApi';\nimport { getTrees } from '../api/TreeApi';\nimport {\n printMessage,\n createProgressIndicator,\n updateProgressIndicator,\n stopProgressIndicator,\n} from './utils/Console';\nimport {\n getProviderByLocationAndId,\n getProviderMetadata,\n} from '../api/Saml2Api';\nimport { encodeBase64Url } from '../api/utils/Base64';\nimport { NodeClassification } from './OpsTypes';\n\nconst containerNodes = ['PageNode', 'CustomPageNode'];\n\nconst scriptedNodes = [\n 'ConfigProviderNode',\n 'ScriptedDecisionNode',\n 'ClientScriptNode',\n 'SocialProviderHandlerNode',\n 'CustomScriptNode',\n];\n\nconst emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];\n\nconst emptyScriptPlaceholder = '[Empty]';\n\n/**\n * Helper to get all SAML2 dependencies for a given node object\n * @param {Object} nodeObject node object\n * @param {[Object]} allProviders array of all saml2 providers objects\n * @param {[Object]} allCirclesOfTrust array of all circle of trust objects\n * @returns {Promise} a promise that resolves to an object containing a saml2 dependencies\n */\nasync function getSaml2NodeDependencies(\n nodeObject,\n allProviders,\n allCirclesOfTrust\n) {\n const samlProperties = ['metaAlias', 'idpEntityId'];\n const saml2EntityPromises = [];\n for (const samlProperty of samlProperties) {\n // In the following line nodeObject[samlProperty] will look like '/alpha/iSPAzure'.\n const entityId =\n samlProperty === 'metaAlias'\n ? _.last(nodeObject[samlProperty].split('/'))\n : nodeObject[samlProperty];\n const entity = _.find(allProviders, { entityId });\n if (entity) {\n try {\n const providerResponse = await getProviderByLocationAndId(\n entity.location,\n entity._id\n );\n /**\n * Adding entityLocation here to the entityResponse because the import tool\n * needs to know whether the saml2 entity is remote or not (this will be removed\n * from the config before importing see updateSaml2Entity and createSaml2Entity functions).\n * Importing a remote saml2 entity is a slightly different request (see createSaml2Entity).\n */\n providerResponse.entityLocation = entity.location;\n\n if (entity.location === 'remote') {\n // get the xml representation of this entity and add it to the entityResponse;\n const metaDataResponse = await getProviderMetadata(\n providerResponse.entityId\n );\n providerResponse.base64EntityXML = encodeBase64Url(metaDataResponse);\n }\n saml2EntityPromises.push(providerResponse);\n } catch (error) {\n printMessage(error.message, 'error');\n }\n }\n }\n try {\n const saml2EntitiesPromisesResults = await Promise.all(saml2EntityPromises);\n const saml2Entities = [];\n for (const saml2Entity of saml2EntitiesPromisesResults) {\n if (saml2Entity) {\n saml2Entities.push(saml2Entity);\n }\n }\n const samlEntityIds = _.map(\n saml2Entities,\n (saml2EntityConfig) => `${saml2EntityConfig.entityId}|saml2`\n );\n const circlesOfTrust = _.filter(allCirclesOfTrust, (circleOfTrust) => {\n let hasEntityId = false;\n for (const trustedProvider of circleOfTrust.trustedProviders) {\n if (!hasEntityId && samlEntityIds.includes(trustedProvider)) {\n hasEntityId = true;\n }\n }\n return hasEntityId;\n });\n const saml2NodeDependencies = {\n saml2Entities,\n circlesOfTrust,\n };\n return saml2NodeDependencies;\n } catch (error) {\n printMessage(error.message, 'error');\n const saml2NodeDependencies = {\n saml2Entities: [],\n circlesOfTrust: [],\n };\n return saml2NodeDependencies;\n }\n}\n\n/**\n * Find all node configuration objects that are no longer referenced by any tree\n * @returns {Promise<unknown[]>} a promise that resolves to an array of orphaned nodes\n */\nexport async function findOrphanedNodes(): Promise<unknown[]> {\n const allNodes = [];\n const orphanedNodes = [];\n let types = [];\n const allJourneys = (await getTrees()).result;\n let errorMessage = '';\n const errorTypes = [];\n\n createProgressIndicator(\n undefined,\n `Counting total nodes...`,\n 'indeterminate'\n );\n try {\n types = (await getNodeTypes()).result;\n } catch (error) {\n printMessage('Error retrieving all available node types:', 'error');\n printMessage(error.response.data, 'error');\n return [];\n }\n for (const type of types) {\n try {\n // eslint-disable-next-line no-await-in-loop, no-loop-func\n const nodes = (await getNodesByType(type._id)).result;\n for (const node of nodes) {\n allNodes.push(node);\n updateProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`\n );\n }\n } catch (error) {\n errorTypes.push(type._id);\n errorMessage = ` (Skipped type(s): ${errorTypes})`['yellow'];\n updateProgressIndicator(`${allNodes.length} total nodes${errorMessage}`);\n }\n }\n if (errorTypes.length > 0) {\n stopProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`,\n 'warn'\n );\n } else {\n stopProgressIndicator(`${allNodes.length} total nodes`, 'success');\n }\n\n createProgressIndicator(\n undefined,\n 'Counting active nodes...',\n 'indeterminate'\n );\n const activeNodes = [];\n for (const journey of allJourneys) {\n for (const nodeId in journey.nodes) {\n if ({}.hasOwnProperty.call(journey.nodes, nodeId)) {\n activeNodes.push(nodeId);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n const node = journey.nodes[nodeId];\n if (containerNodes.includes(node.nodeType)) {\n const containerNode = await getNode(nodeId, node.nodeType);\n for (const innerNode of containerNode.nodes) {\n activeNodes.push(innerNode._id);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n }\n }\n }\n }\n }\n stopProgressIndicator(`${activeNodes.length} active nodes`, 'success');\n\n createProgressIndicator(\n undefined,\n 'Calculating orphaned nodes...',\n 'indeterminate'\n );\n const diff = allNodes.filter((x) => !activeNodes.includes(x._id));\n for (const orphanedNode of diff) {\n orphanedNodes.push(orphanedNode);\n }\n stopProgressIndicator(`${orphanedNodes.length} orphaned nodes`, 'success');\n return orphanedNodes;\n}\n\n/**\n * Remove orphaned nodes\n * @param {[Object]} orphanedNodes Pass in an array of orphaned node configuration objects to remove\n * @returns {Promise<unknown[]>} a promise that resolves to an array nodes that encountered errors deleting\n */\nexport async function removeOrphanedNodes(\n orphanedNodes: unknown[]\n): Promise<unknown[]> {\n const errorNodes = [];\n createProgressIndicator(orphanedNodes.length, 'Removing orphaned nodes...');\n for (const node of orphanedNodes) {\n updateProgressIndicator(`Removing ${node['_id']}...`);\n try {\n // eslint-disable-next-line no-await-in-loop\n await deleteNode(node['_id'], node['_type']['_id']);\n } catch (deleteError) {\n errorNodes.push(node);\n printMessage(` ${deleteError}`, 'error');\n }\n }\n stopProgressIndicator(`Removed ${orphanedNodes.length} orphaned nodes.`);\n return errorNodes;\n}\n\nconst OOTB_NODE_TYPES_7 = [\n 'AcceptTermsAndConditionsNode',\n 'AccountActiveDecisionNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousSessionUpgradeNode',\n 'AnonymousUserNode',\n 'AttributeCollectorNode',\n 'AttributePresentDecisionNode',\n 'AttributeValueDecisionNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'ConsentNode',\n 'CookiePresenceDecisionNode',\n 'CreateObjectNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'DeviceGeoFencingNode',\n 'DeviceLocationMatchNode',\n 'DeviceMatchNode',\n 'DeviceProfileCollectorNode',\n 'DeviceSaveNode',\n 'DeviceTamperingVerificationNode',\n 'DisplayUserNameNode',\n 'EmailSuspendNode',\n 'EmailTemplateNode',\n 'IdentifyExistingUserNode',\n 'IncrementLoginCountNode',\n 'InnerTreeEvaluatorNode',\n 'IotAuthenticationNode',\n 'IotRegistrationNode',\n 'KbaCreateNode',\n 'KbaDecisionNode',\n 'KbaVerifyNode',\n 'LdapDecisionNode',\n 'LoginCountDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PatchObjectNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProfileCompletenessDecisionNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'QueryFilterDecisionNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RequiredAttributesDecisionNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SelectIdPNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'SocialProviderHandlerNode',\n 'TermsAndConditionsDecisionNode',\n 'TimeSinceDecisionNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'ValidatedPasswordNode',\n 'ValidatedUsernameNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnDeviceStorageNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n 'product-CertificateCollectorNode',\n 'product-CertificateUserExtractorNode',\n 'product-CertificateValidationNode',\n 'product-KerberosNode',\n 'product-ReCaptchaNode',\n 'product-Saml2Node',\n 'product-WriteFederationInformationNode',\n];\n\nconst OOTB_NODE_TYPES_7_1 = [\n 'PushRegistrationNode',\n 'GetAuthenticatorAppNode',\n 'MultiFactorRegistrationOptionsNode',\n 'OptOutMultiFactorAuthenticationNode',\n].concat(OOTB_NODE_TYPES_7);\n\nconst OOTB_NODE_TYPES_7_2 = [\n 'OathRegistrationNode',\n 'OathTokenVerifierNode',\n 'PassthroughAuthenticationNode',\n 'ConfigProviderNode',\n 'DebugNode',\n].concat(OOTB_NODE_TYPES_7_1);\n\nconst OOTB_NODE_TYPES_7_3 = [].concat(OOTB_NODE_TYPES_7_2);\n\nconst OOTB_NODE_TYPES_6_5 = [\n 'AbstractSocialAuthLoginNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousUserNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'CookiePresenceDecisionNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'InnerTreeEvaluatorNode',\n 'LdapDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n];\n\nconst OOTB_NODE_TYPES_6 = [\n 'AbstractSocialAuthLoginNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousUserNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'CookiePresenceDecisionNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'InnerTreeEvaluatorNode',\n 'LdapDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n];\n\nconst CLOUD_ONLY_NODE_TYPES = [\n 'IdentityStoreDecisionNode',\n 'AutonomousAccessSignalNode',\n 'AutonomousAccessDecisionNode',\n 'AutonomousAccessResultNode',\n];\n\nconst PREMIUM_NODE_TYPES = [\n 'AutonomousAccessSignalNode',\n 'AutonomousAccessDecisionNode',\n 'AutonomousAccessResultNode',\n];\n\n/**\n * Analyze if a node is a premium node.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is premium, false otherwise.\n */\nexport function isPremiumNode(nodeType: string): boolean {\n return PREMIUM_NODE_TYPES.includes(nodeType);\n}\n\n/**\n * Analyze if a node is a cloud-only node.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is cloud-only, false otherwise.\n */\nexport function isCloudOnlyNode(nodeType: string): boolean {\n return CLOUD_ONLY_NODE_TYPES.includes(nodeType);\n}\n\n/**\n * Analyze if a node is custom.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is custom, false otherwise.\n */\nexport function isCustomNode(nodeType: string): boolean {\n let ootbNodeTypes = [];\n switch (storage.session.getAmVersion()) {\n case '7.1.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_1.slice(0);\n break;\n case '7.2.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_2.slice(0);\n break;\n case '7.3.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_3.slice(0);\n break;\n case '7.0.0':\n case '7.0.1':\n case '7.0.2':\n ootbNodeTypes = OOTB_NODE_TYPES_7.slice(0);\n break;\n case '6.5.3':\n case '6.5.2.3':\n case '6.5.2.2':\n case '6.5.2.1':\n case '6.5.2':\n case '6.5.1':\n case '6.5.0.2':\n case '6.5.0.1':\n ootbNodeTypes = OOTB_NODE_TYPES_6_5.slice(0);\n break;\n case '6.0.0.7':\n case '6.0.0.6':\n case '6.0.0.5':\n case '6.0.0.4':\n case '6.0.0.3':\n case '6.0.0.2':\n case '6.0.0.1':\n case '6.0.0':\n ootbNodeTypes = OOTB_NODE_TYPES_6.slice(0);\n break;\n default:\n return true;\n }\n return (\n !ootbNodeTypes.includes(nodeType) &&\n !isPremiumNode(nodeType) &&\n !isCloudOnlyNode(nodeType)\n );\n}\n\n/**\n * Get a node's classifications, which can be one or multiple of:\n * - standard: can run on any instance of a ForgeRock platform\n * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud\n * - premium: utilizes nodes, which come at a premium\n * @param {string} nodeType Node type\n * @returns {NodeClassification[]} an array of one or multiple classifications\n */\nexport function getNodeClassification(nodeType: string): NodeClassification[] {\n const classifications: NodeClassification[] = [];\n const premium = isPremiumNode(nodeType);\n const custom = isCustomNode(nodeType);\n const cloud = isCloudOnlyNode(nodeType);\n if (custom) {\n classifications.push(NodeClassification.CUSTOM);\n } else if (cloud) {\n classifications.push(NodeClassification.CLOUD);\n } else {\n classifications.push(NodeClassification.STANDARD);\n }\n if (premium) classifications.push(NodeClassification.PREMIUM);\n return classifications;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/NodeOps.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAoGhD;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAgF5D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,OAAO,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAepB;AAsOD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA6CtD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAc5E","file":"NodeOps.d.ts","sourcesContent":["import _ from 'lodash';\nimport * as state from '../shared/State';\nimport {\n getNode,\n deleteNode,\n getNodeTypes,\n getNodesByType,\n} from '../api/NodeApi';\nimport { getTrees } from '../api/TreeApi';\nimport {\n printMessage,\n createProgressIndicator,\n updateProgressIndicator,\n stopProgressIndicator,\n} from './utils/Console';\nimport {\n getProviderByLocationAndId,\n getProviderMetadata,\n} from '../api/Saml2Api';\nimport { encodeBase64Url } from '../api/utils/Base64';\nimport { NodeClassification } from './OpsTypes';\n\nconst containerNodes = ['PageNode', 'CustomPageNode'];\n\nconst scriptedNodes = [\n 'ConfigProviderNode',\n 'ScriptedDecisionNode',\n 'ClientScriptNode',\n 'SocialProviderHandlerNode',\n 'CustomScriptNode',\n];\n\nconst emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];\n\nconst emptyScriptPlaceholder = '[Empty]';\n\n/**\n * Helper to get all SAML2 dependencies for a given node object\n * @param {Object} nodeObject node object\n * @param {[Object]} allProviders array of all saml2 providers objects\n * @param {[Object]} allCirclesOfTrust array of all circle of trust objects\n * @returns {Promise} a promise that resolves to an object containing a saml2 dependencies\n */\nasync function getSaml2NodeDependencies(\n nodeObject,\n allProviders,\n allCirclesOfTrust\n) {\n const samlProperties = ['metaAlias', 'idpEntityId'];\n const saml2EntityPromises = [];\n for (const samlProperty of samlProperties) {\n // In the following line nodeObject[samlProperty] will look like '/alpha/iSPAzure'.\n const entityId =\n samlProperty === 'metaAlias'\n ? _.last(nodeObject[samlProperty].split('/'))\n : nodeObject[samlProperty];\n const entity = _.find(allProviders, { entityId });\n if (entity) {\n try {\n const providerResponse = await getProviderByLocationAndId(\n entity.location,\n entity._id\n );\n /**\n * Adding entityLocation here to the entityResponse because the import tool\n * needs to know whether the saml2 entity is remote or not (this will be removed\n * from the config before importing see updateSaml2Entity and createSaml2Entity functions).\n * Importing a remote saml2 entity is a slightly different request (see createSaml2Entity).\n */\n providerResponse.entityLocation = entity.location;\n\n if (entity.location === 'remote') {\n // get the xml representation of this entity and add it to the entityResponse;\n const metaDataResponse = await getProviderMetadata(\n providerResponse.entityId\n );\n providerResponse.base64EntityXML = encodeBase64Url(metaDataResponse);\n }\n saml2EntityPromises.push(providerResponse);\n } catch (error) {\n printMessage(error.message, 'error');\n }\n }\n }\n try {\n const saml2EntitiesPromisesResults = await Promise.all(saml2EntityPromises);\n const saml2Entities = [];\n for (const saml2Entity of saml2EntitiesPromisesResults) {\n if (saml2Entity) {\n saml2Entities.push(saml2Entity);\n }\n }\n const samlEntityIds = _.map(\n saml2Entities,\n (saml2EntityConfig) => `${saml2EntityConfig.entityId}|saml2`\n );\n const circlesOfTrust = _.filter(allCirclesOfTrust, (circleOfTrust) => {\n let hasEntityId = false;\n for (const trustedProvider of circleOfTrust.trustedProviders) {\n if (!hasEntityId && samlEntityIds.includes(trustedProvider)) {\n hasEntityId = true;\n }\n }\n return hasEntityId;\n });\n const saml2NodeDependencies = {\n saml2Entities,\n circlesOfTrust,\n };\n return saml2NodeDependencies;\n } catch (error) {\n printMessage(error.message, 'error');\n const saml2NodeDependencies = {\n saml2Entities: [],\n circlesOfTrust: [],\n };\n return saml2NodeDependencies;\n }\n}\n\n/**\n * Find all node configuration objects that are no longer referenced by any tree\n * @returns {Promise<unknown[]>} a promise that resolves to an array of orphaned nodes\n */\nexport async function findOrphanedNodes(): Promise<unknown[]> {\n const allNodes = [];\n const orphanedNodes = [];\n let types = [];\n const allJourneys = (await getTrees()).result;\n let errorMessage = '';\n const errorTypes = [];\n\n createProgressIndicator(\n undefined,\n `Counting total nodes...`,\n 'indeterminate'\n );\n try {\n types = (await getNodeTypes()).result;\n } catch (error) {\n printMessage('Error retrieving all available node types:', 'error');\n printMessage(error.response.data, 'error');\n return [];\n }\n for (const type of types) {\n try {\n // eslint-disable-next-line no-await-in-loop, no-loop-func\n const nodes = (await getNodesByType(type._id)).result;\n for (const node of nodes) {\n allNodes.push(node);\n updateProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`\n );\n }\n } catch (error) {\n errorTypes.push(type._id);\n errorMessage = ` (Skipped type(s): ${errorTypes})`['yellow'];\n updateProgressIndicator(`${allNodes.length} total nodes${errorMessage}`);\n }\n }\n if (errorTypes.length > 0) {\n stopProgressIndicator(\n `${allNodes.length} total nodes${errorMessage}`,\n 'warn'\n );\n } else {\n stopProgressIndicator(`${allNodes.length} total nodes`, 'success');\n }\n\n createProgressIndicator(\n undefined,\n 'Counting active nodes...',\n 'indeterminate'\n );\n const activeNodes = [];\n for (const journey of allJourneys) {\n for (const nodeId in journey.nodes) {\n if ({}.hasOwnProperty.call(journey.nodes, nodeId)) {\n activeNodes.push(nodeId);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n const node = journey.nodes[nodeId];\n if (containerNodes.includes(node.nodeType)) {\n const containerNode = await getNode(nodeId, node.nodeType);\n for (const innerNode of containerNode.nodes) {\n activeNodes.push(innerNode._id);\n updateProgressIndicator(`${activeNodes.length} active nodes`);\n }\n }\n }\n }\n }\n stopProgressIndicator(`${activeNodes.length} active nodes`, 'success');\n\n createProgressIndicator(\n undefined,\n 'Calculating orphaned nodes...',\n 'indeterminate'\n );\n const diff = allNodes.filter((x) => !activeNodes.includes(x._id));\n for (const orphanedNode of diff) {\n orphanedNodes.push(orphanedNode);\n }\n stopProgressIndicator(`${orphanedNodes.length} orphaned nodes`, 'success');\n return orphanedNodes;\n}\n\n/**\n * Remove orphaned nodes\n * @param {[Object]} orphanedNodes Pass in an array of orphaned node configuration objects to remove\n * @returns {Promise<unknown[]>} a promise that resolves to an array nodes that encountered errors deleting\n */\nexport async function removeOrphanedNodes(\n orphanedNodes: unknown[]\n): Promise<unknown[]> {\n const errorNodes = [];\n createProgressIndicator(orphanedNodes.length, 'Removing orphaned nodes...');\n for (const node of orphanedNodes) {\n updateProgressIndicator(`Removing ${node['_id']}...`);\n try {\n // eslint-disable-next-line no-await-in-loop\n await deleteNode(node['_id'], node['_type']['_id']);\n } catch (deleteError) {\n errorNodes.push(node);\n printMessage(` ${deleteError}`, 'error');\n }\n }\n stopProgressIndicator(`Removed ${orphanedNodes.length} orphaned nodes.`);\n return errorNodes;\n}\n\nconst OOTB_NODE_TYPES_7 = [\n 'AcceptTermsAndConditionsNode',\n 'AccountActiveDecisionNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousSessionUpgradeNode',\n 'AnonymousUserNode',\n 'AttributeCollectorNode',\n 'AttributePresentDecisionNode',\n 'AttributeValueDecisionNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'ConsentNode',\n 'CookiePresenceDecisionNode',\n 'CreateObjectNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'DeviceGeoFencingNode',\n 'DeviceLocationMatchNode',\n 'DeviceMatchNode',\n 'DeviceProfileCollectorNode',\n 'DeviceSaveNode',\n 'DeviceTamperingVerificationNode',\n 'DisplayUserNameNode',\n 'EmailSuspendNode',\n 'EmailTemplateNode',\n 'IdentifyExistingUserNode',\n 'IncrementLoginCountNode',\n 'InnerTreeEvaluatorNode',\n 'IotAuthenticationNode',\n 'IotRegistrationNode',\n 'KbaCreateNode',\n 'KbaDecisionNode',\n 'KbaVerifyNode',\n 'LdapDecisionNode',\n 'LoginCountDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PatchObjectNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProfileCompletenessDecisionNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'QueryFilterDecisionNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RequiredAttributesDecisionNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SelectIdPNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'SocialProviderHandlerNode',\n 'TermsAndConditionsDecisionNode',\n 'TimeSinceDecisionNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'ValidatedPasswordNode',\n 'ValidatedUsernameNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnDeviceStorageNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n 'product-CertificateCollectorNode',\n 'product-CertificateUserExtractorNode',\n 'product-CertificateValidationNode',\n 'product-KerberosNode',\n 'product-ReCaptchaNode',\n 'product-Saml2Node',\n 'product-WriteFederationInformationNode',\n];\n\nconst OOTB_NODE_TYPES_7_1 = [\n 'PushRegistrationNode',\n 'GetAuthenticatorAppNode',\n 'MultiFactorRegistrationOptionsNode',\n 'OptOutMultiFactorAuthenticationNode',\n].concat(OOTB_NODE_TYPES_7);\n\nconst OOTB_NODE_TYPES_7_2 = [\n 'OathRegistrationNode',\n 'OathTokenVerifierNode',\n 'PassthroughAuthenticationNode',\n 'ConfigProviderNode',\n 'DebugNode',\n].concat(OOTB_NODE_TYPES_7_1);\n\nconst OOTB_NODE_TYPES_7_3 = [].concat(OOTB_NODE_TYPES_7_2);\n\nconst OOTB_NODE_TYPES_6_5 = [\n 'AbstractSocialAuthLoginNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousUserNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'CookiePresenceDecisionNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'InnerTreeEvaluatorNode',\n 'LdapDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n];\n\nconst OOTB_NODE_TYPES_6 = [\n 'AbstractSocialAuthLoginNode',\n 'AccountLockoutNode',\n 'AgentDataStoreDecisionNode',\n 'AnonymousUserNode',\n 'AuthLevelDecisionNode',\n 'ChoiceCollectorNode',\n 'CookiePresenceDecisionNode',\n 'CreatePasswordNode',\n 'DataStoreDecisionNode',\n 'InnerTreeEvaluatorNode',\n 'LdapDecisionNode',\n 'MessageNode',\n 'MetadataNode',\n 'MeterNode',\n 'ModifyAuthLevelNode',\n 'OneTimePasswordCollectorDecisionNode',\n 'OneTimePasswordGeneratorNode',\n 'OneTimePasswordSmsSenderNode',\n 'OneTimePasswordSmtpSenderNode',\n 'PageNode',\n 'PasswordCollectorNode',\n 'PersistentCookieDecisionNode',\n 'PollingWaitNode',\n 'ProvisionDynamicAccountNode',\n 'ProvisionIdmAccountNode',\n 'PushAuthenticationSenderNode',\n 'PushResultVerifierNode',\n 'RecoveryCodeCollectorDecisionNode',\n 'RecoveryCodeDisplayNode',\n 'RegisterLogoutWebhookNode',\n 'RemoveSessionPropertiesNode',\n 'RetryLimitDecisionNode',\n 'ScriptedDecisionNode',\n 'SessionDataNode',\n 'SetFailureUrlNode',\n 'SetPersistentCookieNode',\n 'SetSessionPropertiesNode',\n 'SetSuccessUrlNode',\n 'SocialFacebookNode',\n 'SocialGoogleNode',\n 'SocialNode',\n 'SocialOAuthIgnoreProfileNode',\n 'SocialOpenIdConnectNode',\n 'TimerStartNode',\n 'TimerStopNode',\n 'UsernameCollectorNode',\n 'WebAuthnAuthenticationNode',\n 'WebAuthnRegistrationNode',\n 'ZeroPageLoginNode',\n];\n\nconst CLOUD_ONLY_NODE_TYPES = [\n 'IdentityStoreDecisionNode',\n 'AutonomousAccessSignalNode',\n 'AutonomousAccessDecisionNode',\n 'AutonomousAccessResultNode',\n];\n\nconst PREMIUM_NODE_TYPES = [\n 'AutonomousAccessSignalNode',\n 'AutonomousAccessDecisionNode',\n 'AutonomousAccessResultNode',\n];\n\n/**\n * Analyze if a node is a premium node.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is premium, false otherwise.\n */\nexport function isPremiumNode(nodeType: string): boolean {\n return PREMIUM_NODE_TYPES.includes(nodeType);\n}\n\n/**\n * Analyze if a node is a cloud-only node.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is cloud-only, false otherwise.\n */\nexport function isCloudOnlyNode(nodeType: string): boolean {\n return CLOUD_ONLY_NODE_TYPES.includes(nodeType);\n}\n\n/**\n * Analyze if a node is custom.\n * @param {string} nodeType Node type\n * @returns {boolean} True if the node type is custom, false otherwise.\n */\nexport function isCustomNode(nodeType: string): boolean {\n let ootbNodeTypes = [];\n switch (state.getAmVersion()) {\n case '7.1.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_1.slice(0);\n break;\n case '7.2.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_2.slice(0);\n break;\n case '7.3.0':\n ootbNodeTypes = OOTB_NODE_TYPES_7_3.slice(0);\n break;\n case '7.0.0':\n case '7.0.1':\n case '7.0.2':\n ootbNodeTypes = OOTB_NODE_TYPES_7.slice(0);\n break;\n case '6.5.3':\n case '6.5.2.3':\n case '6.5.2.2':\n case '6.5.2.1':\n case '6.5.2':\n case '6.5.1':\n case '6.5.0.2':\n case '6.5.0.1':\n ootbNodeTypes = OOTB_NODE_TYPES_6_5.slice(0);\n break;\n case '6.0.0.7':\n case '6.0.0.6':\n case '6.0.0.5':\n case '6.0.0.4':\n case '6.0.0.3':\n case '6.0.0.2':\n case '6.0.0.1':\n case '6.0.0':\n ootbNodeTypes = OOTB_NODE_TYPES_6.slice(0);\n break;\n default:\n return true;\n }\n return (\n !ootbNodeTypes.includes(nodeType) &&\n !isPremiumNode(nodeType) &&\n !isCloudOnlyNode(nodeType)\n );\n}\n\n/**\n * Get a node's classifications, which can be one or multiple of:\n * - standard: can run on any instance of a ForgeRock platform\n * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud\n * - premium: utilizes nodes, which come at a premium\n * @param {string} nodeType Node type\n * @returns {NodeClassification[]} an array of one or multiple classifications\n */\nexport function getNodeClassification(nodeType: string): NodeClassification[] {\n const classifications: NodeClassification[] = [];\n const premium = isPremiumNode(nodeType);\n const custom = isCustomNode(nodeType);\n const cloud = isCloudOnlyNode(nodeType);\n if (custom) {\n classifications.push(NodeClassification.CUSTOM);\n } else if (cloud) {\n classifications.push(NodeClassification.CLOUD);\n } else {\n classifications.push(NodeClassification.STANDARD);\n }\n if (premium) classifications.push(NodeClassification.PREMIUM);\n return classifications;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/OAuth2ClientOps.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,UAAQ,iBAmDnD;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,EAAE,KAAA,EAAE,IAAI,KAAA,iBAStD;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,KAAA,iBAgBnD;AAED;;GAEG;AACH,wBAAsB,0BAA0B,kBAQ/C;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,KAAA,iBAwBrD","file":"OAuth2ClientOps.d.ts","sourcesContent":["import fs from 'fs';\nimport { createTable, printMessage } from './utils/Console';\nimport {\n getTypedFilename,\n saveToFile,\n titleCase,\n validateImport,\n} from './utils/ExportImportUtils';\nimport
|
|
1
|
+
{"version":3,"sources":["../src/ops/OAuth2ClientOps.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,UAAQ,iBAmDnD;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,EAAE,KAAA,EAAE,IAAI,KAAA,iBAStD;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,KAAA,iBAgBnD;AAED;;GAEG;AACH,wBAAsB,0BAA0B,kBAQ/C;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,KAAA,iBAwBrD","file":"OAuth2ClientOps.d.ts","sourcesContent":["import fs from 'fs';\nimport { createTable, printMessage } from './utils/Console';\nimport {\n getTypedFilename,\n saveToFile,\n titleCase,\n validateImport,\n} from './utils/ExportImportUtils';\nimport * as state from '../shared/State';\nimport {\n getOAuth2Client,\n getOAuth2Clients,\n putOAuth2Client,\n} from '../api/OAuth2ClientApi';\nimport { getOAuth2Provider } from '../api/OAuth2ProviderApi';\nimport { getRealmName } from '../api/utils/ApiUtils';\n\n/**\n * List OAuth2 clients\n */\nexport async function listOAuth2Clients(long = false) {\n try {\n const clients = (await getOAuth2Clients()).data.result;\n clients.sort((a, b) => a._id.localeCompare(b._id));\n if (long) {\n const table = createTable([\n 'Client Id',\n 'Status',\n 'Client Type',\n 'Grant Types',\n 'Scopes',\n 'Redirect URIs',\n // 'Description',\n ]);\n const grantTypesMap = {\n authorization_code: 'Authz Code',\n client_credentials: 'Client Creds',\n refresh_token: 'Refresh Token',\n password: 'ROPC',\n 'urn:ietf:params:oauth:grant-type:uma-ticket': 'UMA',\n implicit: 'Implicit',\n 'urn:ietf:params:oauth:grant-type:device_code': 'Device Code',\n 'urn:ietf:params:oauth:grant-type:saml2-bearer': 'SAML2 Bearer',\n 'urn:openid:params:grant-type:ciba': 'CIBA',\n 'urn:ietf:params:oauth:grant-type:token-exchange': 'Token Exchange',\n 'urn:ietf:params:oauth:grant-type:jwt-bearer': 'JWT Bearer',\n };\n clients.forEach((client) => {\n table.push([\n client._id,\n client.coreOAuth2ClientConfig.status === 'Active'\n ? 'Active'['brightGreen']\n : client.coreOAuth2ClientConfig.status.brightRed,\n client.coreOAuth2ClientConfig.clientType,\n client.advancedOAuth2ClientConfig.grantTypes\n .map((type) => grantTypesMap[type])\n .join('\\n'),\n client.coreOAuth2ClientConfig.scopes.join('\\n'),\n client.coreOAuth2ClientConfig.redirectionUris.join('\\n'),\n // wordwrap(client.description, 30),\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n clients.forEach((client) => {\n printMessage(`${client._id}`, 'data');\n });\n }\n } catch (error) {\n printMessage(`Error listing applications - ${error}`, 'error');\n }\n}\n\n/**\n * Export OAuth2 client to file\n * @param {String} id client id\n * @param {String} file file name\n */\nexport async function exportOAuth2ClientToFile(id, file) {\n let fileName = getTypedFilename(id, 'oauth2.app');\n if (file) {\n fileName = file;\n }\n const oauth2Service = (await getOAuth2Provider()).data;\n const client = (await getOAuth2Client(id)).data;\n client._provider = oauth2Service;\n saveToFile('application', [client], '_id', fileName);\n}\n\n/**\n * Export all OAuth2 clients to file\n * @param {String} file file name\n */\nexport async function exportOAuth2ClientsToFile(file) {\n let fileName = getTypedFilename(\n `all${titleCase(getRealmName(state.getRealm()))}Applications`,\n 'oauth2.app'\n );\n if (file) {\n fileName = file;\n }\n const oauth2Service = (await getOAuth2Provider()).data;\n const clients = (await getOAuth2Clients()).data.result;\n const exportData = [];\n for (const client of clients) {\n client._provider = oauth2Service;\n exportData.push(client);\n }\n saveToFile('application', exportData, '_id', fileName);\n}\n\n/**\n * Export all OAuth2 clients to separate files\n */\nexport async function exportOAuth2ClientsToFiles() {\n const oauth2Service = (await getOAuth2Provider()).data;\n const clients = (await getOAuth2Clients()).data.result;\n for (const client of clients) {\n client._provider = oauth2Service;\n const fileName = getTypedFilename(client._id, 'oauth2.app');\n saveToFile('application', [client], '_id', fileName);\n }\n}\n\n/**\n * Import OAuth2 clients from file\n * @param {String} file file name\n */\nexport async function importOAuth2ClientsFromFile(file) {\n fs.readFile(file, 'utf8', async (err, data) => {\n if (err) throw err;\n const applicationData = JSON.parse(data);\n if (validateImport(applicationData.meta)) {\n for (const id in applicationData.application) {\n if (\n Object.prototype.hasOwnProperty.call(applicationData.application, id)\n ) {\n delete applicationData.application[id]._provider;\n delete applicationData.application[id]._rev;\n try {\n await putOAuth2Client(id, applicationData.application[id]);\n printMessage(`Imported ${id}`);\n } catch (error) {\n printMessage(`${error.message}`, 'error');\n printMessage(error.response.status, 'error');\n }\n }\n }\n } else {\n printMessage('Import validation failed...', 'error');\n }\n });\n}\n"]}
|
package/types/ops/OpsTypes.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface ExportMetaData {
|
|
|
34
34
|
exportToolVersion: string;
|
|
35
35
|
}
|
|
36
36
|
export interface SingleTreeExportInterface {
|
|
37
|
-
meta?:
|
|
37
|
+
meta?: ExportMetaData;
|
|
38
38
|
innerNodes?: Record<string, NodeSkeleton>;
|
|
39
39
|
innernodes?: Record<string, NodeSkeleton>;
|
|
40
40
|
nodes: Record<string, NodeSkeleton>;
|
|
@@ -54,6 +54,15 @@ export interface AgentExportInterface {
|
|
|
54
54
|
meta?: Record<string, ExportMetaData>;
|
|
55
55
|
agents: Record<string, AgentSkeleton>;
|
|
56
56
|
}
|
|
57
|
+
export interface Saml2ExportInterface {
|
|
58
|
+
meta?: ExportMetaData;
|
|
59
|
+
script: Record<string, ScriptSkeleton>;
|
|
60
|
+
saml: {
|
|
61
|
+
hosted: Record<string, Saml2ProviderSkeleton>;
|
|
62
|
+
remote: Record<string, Saml2ProviderSkeleton>;
|
|
63
|
+
metadata: Record<string, string[]>;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
57
66
|
export interface ServiceExportInterface {
|
|
58
67
|
meta?: Record<string, ExportMetaData>;
|
|
59
68
|
service: Record<string, AmServiceSkeleton>;
|
|
@@ -76,3 +85,10 @@ export declare enum JourneyClassification {
|
|
|
76
85
|
CLOUD = "cloud",
|
|
77
86
|
PREMIUM = "premium"
|
|
78
87
|
}
|
|
88
|
+
export interface MultiOpStatusInterface {
|
|
89
|
+
total: number;
|
|
90
|
+
successes: number;
|
|
91
|
+
warnings: number;
|
|
92
|
+
failures: number;
|
|
93
|
+
message?: string;
|
|
94
|
+
}
|