@rockcarver/frodo-lib 0.16.2-9 → 0.17.1
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 +222 -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 +16 -67
- 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 +5 -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 +2 -159
- package/cjs/ops/AgentOps.js.map +1 -1
- package/cjs/ops/AgentOps.test.js.map +1 -1
- package/cjs/ops/AuthenticateOps.js +266 -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 +139 -370
- 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 +11 -107
- package/cjs/ops/Saml2Ops.js.map +1 -1
- package/cjs/ops/Saml2Ops.test.js.map +1 -1
- package/cjs/ops/ScriptOps.js +26 -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 +161 -83
- 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/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 +25 -29
- 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 +5 -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 +37 -80
- package/esm/ops/AgentOps.test.mjs +5 -45
- package/esm/ops/AuthenticateOps.mjs +242 -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 +146 -289
- package/esm/ops/JourneyOps.test.mjs +29 -27
- 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 +28 -62
- package/esm/ops/Saml2Ops.test.mjs +20 -23
- package/esm/ops/ScriptOps.mjs +29 -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 +147 -31
- 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/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 +36 -26
- package/types/api/AgentApi.d.ts.map +1 -1
- package/types/api/ApiTypes.d.ts +15 -16
- 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.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/index.d.ts +11 -5
- package/types/index.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 +1 -1
- package/types/ops/OpsTypes.d.ts.map +1 -1
- package/types/ops/OrganizationOps.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 +19 -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
package/types/index.d.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
export * as AgentRaw from './api/AgentApi';
|
|
2
|
-
export * as
|
|
2
|
+
export * as AuthenticateRaw from './api/AuthenticateApi';
|
|
3
3
|
export * as NodeRaw from './api/NodeApi';
|
|
4
|
+
export * as OAuth2OIDCApi from './api/OAuth2OIDCApi';
|
|
4
5
|
export * as SecretsRaw from './api/SecretsApi';
|
|
5
6
|
export * as StartupRaw from './api/StartupApi';
|
|
6
|
-
export * as ThemeRaw from './api/ThemeApi';
|
|
7
7
|
export * as TreeRaw from './api/TreeApi';
|
|
8
8
|
export * as TypesRaw from './api/ApiTypes';
|
|
9
9
|
export * as VariablesRaw from './api/VariablesApi';
|
|
10
|
+
export * as IdmConfigRaw from './api/IdmConfigApi';
|
|
10
11
|
export * as Admin from './ops/AdminOps';
|
|
11
12
|
export * as Agent from './ops/AgentOps';
|
|
12
13
|
export * as Authenticate from './ops/AuthenticateOps';
|
|
13
14
|
export * as CirclesOfTrust from './ops/CirclesOfTrustOps';
|
|
14
15
|
export * as ConnectionProfile from './ops/ConnectionProfileOps';
|
|
15
16
|
export * as EmailTemplate from './ops/EmailTemplateOps';
|
|
16
|
-
export * as Idm from './ops/IdmOps';
|
|
17
17
|
export * as Idp from './ops/IdpOps';
|
|
18
|
+
export * as Idm from './ops/IdmOps';
|
|
18
19
|
export * as Journey from './ops/JourneyOps';
|
|
20
|
+
export * as Jose from './ops/JoseOps';
|
|
19
21
|
export * as Log from './ops/LogOps';
|
|
20
22
|
export * as ManagedObject from './ops/ManagedObjectOps';
|
|
21
23
|
export * as Node from './ops/NodeOps';
|
|
@@ -26,12 +28,16 @@ export * as Saml2 from './ops/Saml2Ops';
|
|
|
26
28
|
export * as Script from './ops/ScriptOps';
|
|
27
29
|
export * as Service from './ops/ServiceOps';
|
|
28
30
|
export * as Secrets from './ops/SecretsOps';
|
|
31
|
+
export * as ServiceAccount from './ops/ServiceAccountOps';
|
|
29
32
|
export * as Startup from './ops/StartupOps';
|
|
30
33
|
export * as Theme from './ops/ThemeOps';
|
|
31
34
|
export * as Types from './ops/OpsTypes';
|
|
32
35
|
export * as Variables from './ops/VariablesOps';
|
|
33
36
|
export * as Utils from './ops/utils/OpsUtils';
|
|
37
|
+
export * as ValidationUtils from './ops/utils/ValidationUtils';
|
|
34
38
|
export * as LibVersion from './ops/utils/Version';
|
|
35
39
|
export * as ExportImportUtils from './ops/utils/ExportImportUtils';
|
|
36
|
-
export * as state from './
|
|
37
|
-
export * as
|
|
40
|
+
export * as state from './shared/State';
|
|
41
|
+
export * as constants from './storage/StaticStorage';
|
|
42
|
+
export * as ForgeRockApiMockEngine from './test/mocks/ForgeRockApiMockEngine';
|
|
43
|
+
export { FrodoMockAdapter } from './api/BaseApi';
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,eAAe,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,OAAO,MAAM,eAAe,CAAC;AAIzC,OAAO,KAAK,aAAa,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,OAAO,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAC;AAGnD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,YAAY,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,iBAAiB,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,aAAa,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AACtC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,aAAa,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AACtC,OAAO,KAAK,YAAY,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,YAAY,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,KAAK,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,eAAe,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,iBAAiB,MAAM,+BAA+B,CAAC;AAGnE,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,KAAK,SAAS,MAAM,yBAAyB,CAAC;AAGrD,OAAO,KAAK,sBAAsB,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC","file":"index.d.ts","sourcesContent":["import Color from 'colors';\n\nColor.enable();\n\n// Api Layer\nexport * as AgentRaw from './api/AgentApi';\nexport * as AuthenticateRaw from './api/AuthenticateApi';\nexport * as NodeRaw from './api/NodeApi';\n// TODO: need to figure out if this is the right approach or if we should even\n// use a public oauth2/oidc library. might be ok for now since there is only\n// one place where the cli needs to execute an oauth flow.\nexport * as OAuth2OIDCApi from './api/OAuth2OIDCApi';\nexport * as SecretsRaw from './api/SecretsApi';\nexport * as StartupRaw from './api/StartupApi';\nexport * as TreeRaw from './api/TreeApi';\nexport * as TypesRaw from './api/ApiTypes';\nexport * as VariablesRaw from './api/VariablesApi';\nexport * as IdmConfigRaw from './api/IdmConfigApi';\n\n// Ops Layer\nexport * as Admin from './ops/AdminOps';\nexport * as Agent from './ops/AgentOps';\nexport * as Authenticate from './ops/AuthenticateOps';\nexport * as CirclesOfTrust from './ops/CirclesOfTrustOps';\nexport * as ConnectionProfile from './ops/ConnectionProfileOps';\nexport * as EmailTemplate from './ops/EmailTemplateOps';\nexport * as Idp from './ops/IdpOps';\nexport * as Idm from './ops/IdmOps';\nexport * as Journey from './ops/JourneyOps';\nexport * as Jose from './ops/JoseOps';\nexport * as Log from './ops/LogOps';\nexport * as ManagedObject from './ops/ManagedObjectOps';\nexport * as Node from './ops/NodeOps';\nexport * as OAuth2Client from './ops/OAuth2ClientOps';\nexport * as Organization from './ops/OrganizationOps';\nexport * as Realm from './ops/RealmOps';\nexport * as Saml2 from './ops/Saml2Ops';\nexport * as Script from './ops/ScriptOps';\nexport * as Service from './ops/ServiceOps';\nexport * as Secrets from './ops/SecretsOps';\nexport * as ServiceAccount from './ops/ServiceAccountOps';\nexport * as Startup from './ops/StartupOps';\nexport * as Theme from './ops/ThemeOps';\nexport * as Types from './ops/OpsTypes';\nexport * as Variables from './ops/VariablesOps';\n// TODO: revisit if there are better ways\nexport * as Utils from './ops/utils/OpsUtils';\nexport * as ValidationUtils from './ops/utils/ValidationUtils';\nexport * as LibVersion from './ops/utils/Version';\nexport * as ExportImportUtils from './ops/utils/ExportImportUtils';\n// TODO: reconsider the aproach to pass in state from client\n// lib should be stateless, an aplication should own its state\nexport * as state from './shared/State';\nexport * as constants from './storage/StaticStorage';\n\n// Mock Engine\nexport * as ForgeRockApiMockEngine from './test/mocks/ForgeRockApiMockEngine';\nexport { FrodoMockAdapter } from './api/BaseApi';\n"]}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import { JwkRsa } from './JoseOps';
|
|
2
|
+
/**
|
|
3
|
+
* Get access token for service account
|
|
4
|
+
* @param {string} serviceAccountId UUID of service account
|
|
5
|
+
* @param {JwkRsa} jwk Java Wek Key
|
|
6
|
+
* @returns {string | null} Access token or null
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAccessTokenForServiceAccount(serviceAccountId: string, jwk: JwkRsa): Promise<string | null>;
|
|
1
9
|
/**
|
|
2
10
|
* Get tokens
|
|
3
11
|
* @param {boolean} save true to save a connection profile upon successful authentication, false otherwise
|
|
4
|
-
* @returns {boolean} true if tokens were successfully obtained, false otherwise
|
|
12
|
+
* @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise
|
|
5
13
|
*/
|
|
6
|
-
export declare function getTokens(
|
|
14
|
+
export declare function getTokens(): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/AuthenticateOps.ts"],"names":[],"mappings":"AA4TA;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,UAAQ,oBAyC3C","file":"AuthenticateOps.d.ts","sourcesContent":["import url from 'url';\nimport { createHash, randomBytes } from 'crypto';\nimport readlineSync from 'readline-sync';\nimport { encodeBase64Url } from '../api/utils/Base64';\nimport storage from '../storage/SessionStorage';\nimport * as globalConfig from '../storage/StaticStorage';\nimport { printMessage } from './utils/Console';\nimport { getServerInfo, getServerVersionInfo } from '../api/ServerInfoApi';\nimport { step } from '../api/AuthenticateApi';\nimport { accessToken, authorize } from '../api/OAuth2OIDCApi';\nimport {\n getConnectionProfile,\n saveConnectionProfile,\n} from './ConnectionProfileOps';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst idmAdminScope = 'fr:idm:* openid';\n\nlet adminClientId = 'idmAdminClient';\n\n/**\n * Helper function to get cookie name\n * @returns {String} cookie name\n */\nasync function getCookieName() {\n try {\n return (await getServerInfo()).data.cookieName;\n } catch (error) {\n printMessage(`Error getting cookie name: ${error}`, 'error');\n return null;\n }\n}\n\n/**\n * Helper function to determine if this is a setup mfa prompt in the ID Cloud tenant admin login journey\n * @param {Object} payload response from the previous authentication journey step\n * @returns {Object} an object indicating if 2fa is required and the original payload\n */\nfunction checkAndHandle2FA(payload) {\n // let skippable = false;\n if ('callbacks' in payload) {\n for (const element of payload.callbacks) {\n if (element.type === 'HiddenValueCallback') {\n if (element.input[0].value.includes('skip')) {\n // skippable = true;\n element.input[0].value = 'Skip';\n return {\n need2fa: true,\n payload,\n };\n }\n }\n if (element.type === 'NameCallback') {\n if (element.output[0].value.includes('code')) {\n // skippable = false;\n printMessage('2FA is enabled and required for this user...');\n const code = readlineSync.question(`${element.output[0].value}: `);\n element.input[0].value = code;\n return {\n need2fa: true,\n payload,\n };\n }\n }\n }\n // console.info(\"NO2FA\");\n return {\n need2fa: false,\n payload,\n };\n }\n // console.info(\"NO2FA\");\n return {\n need2fa: false,\n payload,\n };\n}\n\n/**\n * Helper function to set the default realm by deployment type\n * @param {String} deploymentType deployment type\n */\nfunction determineDefaultRealm(deploymentType) {\n if (storage.session.getRealm() === globalConfig.DEFAULT_REALM_KEY) {\n storage.session.setRealm(\n globalConfig.DEPLOYMENT_TYPE_REALM_MAP[deploymentType]\n );\n }\n}\n\n/**\n * Helper function to determine the deployment type\n * @returns {String} deployment type\n */\nasync function determineDeploymentType() {\n const fidcClientId = 'idmAdminClient';\n const forgeopsClientId = 'idm-admin-ui';\n\n const verifier = encodeBase64Url(randomBytes(32));\n const challenge = encodeBase64Url(\n createHash('sha256').update(verifier).digest()\n );\n const challengeMethod = 'S256';\n const redirectURL = url.resolve(\n storage.session.getTenant(),\n redirectUrlTemplate\n );\n\n const config = {\n maxRedirects: 0,\n };\n let bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScope}&response_type=code&client_id=${fidcClientId}&csrf=${storage.session.getCookieValue()}&decision=allow&code_challenge=${challenge}&code_challenge_method=${challengeMethod}`;\n\n let deploymentType = globalConfig.CLASSIC_DEPLOYMENT_TYPE_KEY;\n try {\n await authorize(bodyFormData, config);\n } catch (e) {\n if (\n e.response?.status === 302 &&\n e.response.headers?.location?.indexOf('code=') > -1\n ) {\n printMessage('ForgeRock Identity Cloud ', 'info', false);\n deploymentType = globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY;\n } else {\n try {\n bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScope}&response_type=code&client_id=${forgeopsClientId}&csrf=${storage.session.getCookieValue()}&decision=allow&code_challenge=${challenge}&code_challenge_method=${challengeMethod}`;\n await authorize(bodyFormData, config);\n } catch (ex) {\n if (\n ex.response?.status === 302 &&\n ex.response.headers?.location?.indexOf('code=') > -1\n ) {\n adminClientId = forgeopsClientId;\n printMessage('ForgeOps deployment ', 'info', false);\n deploymentType = globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY;\n } else {\n printMessage('Classic deployment ', 'info', false);\n }\n }\n }\n printMessage('detected.');\n }\n determineDefaultRealm(deploymentType);\n return deploymentType;\n}\n\n/**\n * Helper function to extract the semantic version string from a version info object\n * @param {Object} versionInfo version info object\n * @returns {String} semantic version\n */\nasync function getSemanticVersion(versionInfo) {\n if ('version' in versionInfo) {\n const versionString = versionInfo.version;\n const rx = /([\\d]\\.[\\d]\\.[\\d](\\.[\\d])*)/g;\n const version = versionString.match(rx);\n return version[0];\n }\n throw new Error('Cannot extract semantic version from version info object.');\n}\n\n/**\n * Helper function to authenticate and obtain and store session cookie\n * @returns {String} empty string or null\n */\nasync function authenticate() {\n storage.session.setCookieName(await getCookieName());\n try {\n const config = {\n headers: {\n 'X-OpenAM-Username': storage.session.getUsername(),\n 'X-OpenAM-Password': storage.session.getPassword(),\n },\n };\n const response1 = (await step({}, config)).data;\n const skip2FA = checkAndHandle2FA(response1);\n let response2 = {};\n if (skip2FA.need2fa) {\n response2 = (await step(skip2FA.payload)).data;\n } else {\n response2 = skip2FA.payload;\n }\n if ('tokenId' in response2) {\n storage.session.setCookieValue(response2['tokenId']);\n if (!storage.session.getDeploymentType()) {\n storage.session.setDeploymentType(await determineDeploymentType());\n } else {\n determineDefaultRealm(storage.session.getDeploymentType());\n }\n const versionInfo = (await getServerVersionInfo()).data;\n\n // https://github.com/rockcarver/frodo-cli/issues/109\n // printMessage(`Connected to ${versionInfo.fullVersion}`);\n\n // https://github.com/rockcarver/frodo-cli/issues/102\n printMessage(\n `Connected to [${storage.session.getTenant()}], [${\n !storage.session.getRealm() ? 'alpha' : storage.session.getRealm()\n }] realm, as [${storage.session.getUsername()}]`\n );\n const version = await getSemanticVersion(versionInfo);\n storage.session.setAmVersion(version);\n return '';\n }\n printMessage(`error authenticating`, 'error');\n printMessage('+++ likely cause, bad credentials!!! +++', 'error');\n return null;\n } catch (e) {\n if (e.response?.status === 401) {\n printMessage(`error authenticating - ${e.message}`, 'error');\n printMessage('+++ likely cause, bad credentials +++', 'error');\n }\n if (e.message === 'self signed certificate') {\n printMessage(`error authenticating - ${e.message}`, 'error');\n printMessage('+++ use -k, --insecure option to allow +++', 'error');\n } else {\n printMessage(`error authenticating - ${e.message}`, 'error');\n printMessage(e.response?.data, 'error');\n }\n return null;\n }\n}\n\n/**\n * Helper function to obtain an oauth2 authorization code\n * @param {String} redirectURL oauth2 redirect uri\n * @param {String} codeChallenge PKCE code challenge\n * @param {String} codeChallengeMethod PKCE code challenge method\n * @returns {String} oauth2 authorization code or null\n */\nasync function getAuthCode(redirectURL, codeChallenge, codeChallengeMethod) {\n try {\n const bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScope}&response_type=code&client_id=${adminClientId}&csrf=${storage.session.getCookieValue()}&decision=allow&code_challenge=${codeChallenge}&code_challenge_method=${codeChallengeMethod}`;\n const config = {\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n };\n const response = await authorize(bodyFormData, config);\n if (response.status < 200 || response.status > 399) {\n printMessage('error getting auth code', 'error');\n printMessage(\n 'likely cause: mismatched parameters with OAuth client config',\n 'error'\n );\n return null;\n }\n const redirectLocationURL = response.request.res.responseUrl;\n const queryObject = url.parse(redirectLocationURL, true).query;\n if ('code' in queryObject) {\n return queryObject.code;\n }\n printMessage('auth code not found', 'error');\n return null;\n } catch (error) {\n printMessage(`error getting auth code - ${error.message}`, 'error');\n printMessage(error.response.data, 'error');\n return null;\n }\n}\n\n/**\n * Helper function to obtain oauth2 access token\n * @returns {String} empty string or null\n */\nasync function getAccessToken() {\n try {\n const verifier = encodeBase64Url(randomBytes(32));\n const challenge = encodeBase64Url(\n createHash('sha256').update(verifier).digest()\n );\n const challengeMethod = 'S256';\n const redirectURL = url.resolve(\n storage.session.getTenant(),\n redirectUrlTemplate\n );\n const authCode = await getAuthCode(redirectURL, challenge, challengeMethod);\n if (authCode == null) {\n printMessage('error getting auth code', 'error');\n return null;\n }\n let response = null;\n if (\n storage.session.getDeploymentType() ===\n globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY\n ) {\n const config = {\n auth: {\n username: adminClientId,\n password: adminClientPassword,\n },\n };\n const bodyFormData = `redirect_uri=${redirectURL}&grant_type=authorization_code&code=${authCode}&code_verifier=${verifier}`;\n response = await accessToken(bodyFormData, config);\n } else {\n const bodyFormData = `client_id=${adminClientId}&redirect_uri=${redirectURL}&grant_type=authorization_code&code=${authCode}&code_verifier=${verifier}`;\n response = await accessToken(bodyFormData);\n }\n if (response.status < 200 || response.status > 399) {\n printMessage(`access token call returned ${response.status}`, 'error');\n return null;\n }\n if ('access_token' in response.data) {\n storage.session.setBearerToken(response.data.access_token);\n return '';\n }\n printMessage(\"can't get access token\", 'error');\n return null;\n } catch (e) {\n printMessage('error getting access token - ', 'error');\n return null;\n }\n}\n\n/**\n * Get tokens\n * @param {boolean} save true to save a connection profile upon successful authentication, false otherwise\n * @returns {boolean} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(save = false) {\n let credsFromParameters = true;\n // if username/password on cli are empty, try to read from connections.json\n if (\n storage.session.getUsername() == null &&\n storage.session.getPassword() == null\n ) {\n credsFromParameters = false;\n const conn = await getConnectionProfile();\n if (conn) {\n storage.session.setTenant(conn.tenant);\n storage.session.setUsername(conn.username);\n storage.session.setPassword(conn.password);\n storage.session.setAuthenticationService(conn.authenticationService);\n storage.session.setAuthenticationHeaderOverrides(\n conn.authenticationHeaderOverrides\n );\n } else {\n return false;\n }\n }\n await authenticate();\n if (\n storage.session.getCookieValue() &&\n !storage.session.getBearerToken() &&\n (storage.session.getDeploymentType() ===\n globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY ||\n storage.session.getDeploymentType() ===\n globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY)\n ) {\n await getAccessToken();\n }\n if (save && storage.session.getCookieValue() && credsFromParameters) {\n // valid cookie, which means valid username/password combo. Save it in connections.json\n saveConnectionProfile();\n return true;\n }\n if (!storage.session.getCookieValue()) {\n return false;\n }\n return true;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/AuthenticateOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,MAAM,EAAwB,MAAM,WAAW,CAAC;AAkTzD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,gBAAgB,EAAE,MAAM,EACxB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;AAqCD;;;;GAIG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CA6GlD","file":"AuthenticateOps.d.ts","sourcesContent":["import url from 'url';\nimport { createHash, randomBytes } from 'crypto';\nimport readlineSync from 'readline-sync';\nimport { encodeBase64Url } from '../api/utils/Base64';\nimport * as state from '../shared/State';\nimport * as globalConfig from '../storage/StaticStorage';\nimport { debugMessage, printMessage, verboseMessage } from './utils/Console';\nimport { getServerInfo, getServerVersionInfo } from '../api/ServerInfoApi';\nimport { step } from '../api/AuthenticateApi';\nimport { accessToken, authorize } from '../api/OAuth2OIDCApi';\nimport { getConnectionProfile } from './ConnectionProfileOps';\nimport { v4 } from 'uuid';\nimport { parseUrl } from '../api/utils/ApiUtils';\nimport { JwkRsa, createSignedJwtToken } from './JoseOps';\nimport { getManagedObject } from '../api/ManagedObjectApi';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst idmAdminScopes = 'fr:idm:* openid';\nconst serviceAccountScopes = 'fr:am:* fr:idm:* fr:idc:esv:*';\n\nlet adminClientId = 'idmAdminClient';\n\n/**\n * Helper function to get cookie name\n * @returns {String} cookie name\n */\nasync function determineCookieName() {\n try {\n const { data } = await getServerInfo();\n debugMessage(\n `AuthenticateOps.getCookieName: cookieName=${data.cookieName}`\n );\n return data.cookieName;\n } catch (error) {\n printMessage(`Error getting cookie name: ${error}`, 'error');\n debugMessage(error.stack);\n return null;\n }\n}\n\n/**\n * Helper function to determine if this is a setup mfa prompt in the ID Cloud tenant admin login journey\n * @param {Object} payload response from the previous authentication journey step\n * @returns {Object} an object indicating if 2fa is required and the original payload\n */\nfunction checkAndHandle2FA(payload) {\n // let skippable = false;\n if ('callbacks' in payload) {\n for (const element of payload.callbacks) {\n if (element.type === 'HiddenValueCallback') {\n if (element.input[0].value.includes('skip')) {\n // skippable = true;\n element.input[0].value = 'Skip';\n return {\n need2fa: true,\n payload,\n };\n }\n }\n if (element.type === 'NameCallback') {\n if (element.output[0].value.includes('code')) {\n // skippable = false;\n printMessage('2FA is enabled and required for this user...');\n const code = readlineSync.question(`${element.output[0].value}: `);\n element.input[0].value = code;\n return {\n need2fa: true,\n payload,\n };\n }\n }\n }\n // console.info(\"NO2FA\");\n return {\n need2fa: false,\n payload,\n };\n }\n // console.info(\"NO2FA\");\n return {\n need2fa: false,\n payload,\n };\n}\n\n/**\n * Helper function to set the default realm by deployment type\n * @param {string} deploymentType deployment type\n */\nfunction determineDefaultRealm(deploymentType: string) {\n if (\n !state.getRealm() ||\n state.getRealm() === globalConfig.DEFAULT_REALM_KEY\n ) {\n state.setRealm(globalConfig.DEPLOYMENT_TYPE_REALM_MAP[deploymentType]);\n }\n}\n\n/**\n * Helper function to determine the deployment type\n * @returns {Promise<string>} deployment type\n */\nasync function determineDeploymentType(): Promise<string> {\n const cookieValue = state.getCookieValue();\n // https://bugster.forgerock.org/jira/browse/FRAAS-13018\n // There is a chance that this will be blocked due to security concerns and thus is probably best not to keep active\n // if (!cookieValue && getUseBearerTokenForAmApis()) {\n // const token = await getTokenInfo();\n // cookieValue = token.sessionToken;\n // setCookieValue(cookieValue);\n // }\n\n // if we are using a service account, we know it's cloud\n if (state.getUseBearerTokenForAmApis())\n return globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY;\n\n const fidcClientId = 'idmAdminClient';\n const forgeopsClientId = 'idm-admin-ui';\n\n const verifier = encodeBase64Url(randomBytes(32));\n const challenge = encodeBase64Url(\n createHash('sha256').update(verifier).digest()\n );\n const challengeMethod = 'S256';\n const redirectURL = url.resolve(state.getHost(), redirectUrlTemplate);\n\n const config = {\n maxRedirects: 0,\n headers: {\n [state.getCookieName()]: state.getCookieValue(),\n },\n };\n let bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScopes}&response_type=code&client_id=${fidcClientId}&csrf=${cookieValue}&decision=allow&code_challenge=${challenge}&code_challenge_method=${challengeMethod}`;\n\n let deploymentType = globalConfig.CLASSIC_DEPLOYMENT_TYPE_KEY;\n try {\n await authorize(bodyFormData, config);\n } catch (e) {\n // debugMessage(e.response);\n if (\n e.response?.status === 302 &&\n e.response.headers?.location?.indexOf('code=') > -1\n ) {\n verboseMessage(`ForgeRock Identity Cloud`['brightCyan'] + ` detected.`);\n deploymentType = globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY;\n } else {\n try {\n bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScopes}&response_type=code&client_id=${forgeopsClientId}&csrf=${state.getCookieValue()}&decision=allow&code_challenge=${challenge}&code_challenge_method=${challengeMethod}`;\n await authorize(bodyFormData, config);\n } catch (ex) {\n if (\n ex.response?.status === 302 &&\n ex.response.headers?.location?.indexOf('code=') > -1\n ) {\n adminClientId = forgeopsClientId;\n verboseMessage(`ForgeOps deployment`['brightCyan'] + ` detected.`);\n deploymentType = globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY;\n } else {\n verboseMessage(`Classic deployment`['brightCyan'] + ` detected.`);\n }\n }\n }\n }\n return deploymentType;\n}\n\n/**\n * Helper function to extract the semantic version string from a version info object\n * @param {Object} versionInfo version info object\n * @returns {String} semantic version\n */\nasync function getSemanticVersion(versionInfo) {\n if ('version' in versionInfo) {\n const versionString = versionInfo.version;\n const rx = /([\\d]\\.[\\d]\\.[\\d](\\.[\\d])*)/g;\n const version = versionString.match(rx);\n return version[0];\n }\n throw new Error('Cannot extract semantic version from version info object.');\n}\n\n/**\n * Helper function to authenticate and obtain and store session cookie\n * @returns {string} Session token or null\n */\nasync function authenticate(\n username: string,\n password: string\n): Promise<string> {\n const config = {\n headers: {\n 'X-OpenAM-Username': username,\n 'X-OpenAM-Password': password,\n },\n };\n const response1 = await step({}, config);\n const skip2FA = checkAndHandle2FA(response1);\n let response2 = {};\n if (skip2FA.need2fa) {\n response2 = await step(skip2FA.payload);\n } else {\n response2 = skip2FA.payload;\n }\n if ('tokenId' in response2) {\n return response2['tokenId'] as string;\n }\n return null;\n}\n\n/**\n * Helper function to obtain an oauth2 authorization code\n * @param {string} redirectURL oauth2 redirect uri\n * @param {string} codeChallenge PKCE code challenge\n * @param {string} codeChallengeMethod PKCE code challenge method\n * @returns {string} oauth2 authorization code or null\n */\nasync function getAuthCode(redirectURL, codeChallenge, codeChallengeMethod) {\n try {\n const bodyFormData = `redirect_uri=${redirectURL}&scope=${idmAdminScopes}&response_type=code&client_id=${adminClientId}&csrf=${state.getCookieValue()}&decision=allow&code_challenge=${codeChallenge}&code_challenge_method=${codeChallengeMethod}`;\n const config = {\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n maxRedirects: 0,\n };\n let response = undefined;\n try {\n response = await authorize(bodyFormData, config);\n } catch (error) {\n response = error.response;\n }\n if (response.status < 200 || response.status > 399) {\n printMessage('error getting auth code', 'error');\n printMessage(\n 'likely cause: mismatched parameters with OAuth client config',\n 'error'\n );\n return null;\n }\n const redirectLocationURL = response.headers?.location;\n const queryObject = url.parse(redirectLocationURL, true).query;\n if ('code' in queryObject) {\n return queryObject.code;\n }\n printMessage('auth code not found', 'error');\n return null;\n } catch (error) {\n printMessage(`error getting auth code - ${error.message}`, 'error');\n printMessage(error.response?.data, 'error');\n debugMessage(error.stack);\n return null;\n }\n}\n\n/**\n * Helper function to obtain oauth2 access token\n * @returns {Promise<string | null>} access token or null\n */\nasync function getAccessTokenForUser(): Promise<string | null> {\n try {\n const verifier = encodeBase64Url(randomBytes(32));\n const challenge = encodeBase64Url(\n createHash('sha256').update(verifier).digest()\n );\n const challengeMethod = 'S256';\n const redirectURL = url.resolve(state.getHost(), redirectUrlTemplate);\n const authCode = await getAuthCode(redirectURL, challenge, challengeMethod);\n if (authCode == null) {\n printMessage('error getting auth code', 'error');\n return null;\n }\n let response = null;\n if (state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY) {\n const config = {\n auth: {\n username: adminClientId,\n password: adminClientPassword,\n },\n };\n const bodyFormData = `redirect_uri=${redirectURL}&grant_type=authorization_code&code=${authCode}&code_verifier=${verifier}`;\n response = await accessToken(bodyFormData, config);\n } else {\n const bodyFormData = `client_id=${adminClientId}&redirect_uri=${redirectURL}&grant_type=authorization_code&code=${authCode}&code_verifier=${verifier}`;\n response = await accessToken(bodyFormData);\n }\n if ('access_token' in response.data) {\n return response.data.access_token;\n }\n printMessage('No access token in response.', 'error');\n } catch (error) {\n debugMessage(`Error getting access token for user: ${error}`);\n debugMessage(error.response?.data);\n }\n return null;\n}\n\nfunction createPayload(serviceAccountId: string) {\n const u = parseUrl(state.getHost());\n const aud = `${u.origin}:${\n u.port ? u.port : u.protocol === 'https' ? '443' : '80'\n }${u.pathname}/oauth2/access_token`;\n\n // Cross platform way of setting JWT expiry time 3 minutes in the future, expressed as number of seconds since EPOCH\n const exp = Math.floor(new Date().getTime() / 1000 + 180);\n\n // A unique ID for the JWT which is required when requesting the openid scope\n const jti = v4();\n\n const iss = serviceAccountId;\n const sub = serviceAccountId;\n\n // Create the payload for our bearer token\n const payload = { iss, sub, aud, exp, jti };\n\n return payload;\n}\n\n/**\n * Get access token for service account\n * @param {string} serviceAccountId UUID of service account\n * @param {JwkRsa} jwk Java Wek Key\n * @returns {string | null} Access token or null\n */\nexport async function getAccessTokenForServiceAccount(\n serviceAccountId: string,\n jwk: JwkRsa\n): Promise<string | null> {\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: start`);\n const payload = createPayload(serviceAccountId);\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: payload:`);\n debugMessage(payload);\n const jwt = await createSignedJwtToken(payload, jwk);\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: jwt:`);\n debugMessage(jwt);\n const bodyFormData = `assertion=${jwt}&client_id=service-account&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=${serviceAccountScopes}`;\n const response = await accessToken(bodyFormData);\n if ('access_token' in response.data) {\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: token:`);\n debugMessage(response.data.access_token);\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: end`);\n return response.data.access_token;\n }\n debugMessage(\n `AuthenticateOps.getAccessTokenForServiceAccount: No access token in response.`\n );\n debugMessage(`AuthenticateOps.getAccessTokenForServiceAccount: end`);\n return null;\n}\n\nasync function determineDeploymentTypeAndDefaultRealmAndVersion() {\n debugMessage(\n `AuthenticateOps.determineDeploymentTypeAndDefaultRealmAndVersion: start`\n );\n if (!state.getDeploymentType()) {\n state.setDeploymentType(await determineDeploymentType());\n }\n determineDefaultRealm(state.getDeploymentType());\n debugMessage(\n `AuthenticateOps.determineDeploymentTypeAndDefaultRealmAndVersion: realm=${state.getRealm()}, type=${state.getDeploymentType()}`\n );\n\n const versionInfo = (await getServerVersionInfo()).data;\n\n // https://github.com/rockcarver/frodo-cli/issues/109\n debugMessage(`Full version: ${versionInfo.fullVersion}`);\n\n const version = await getSemanticVersion(versionInfo);\n state.setAmVersion(version);\n debugMessage(\n `AuthenticateOps.determineDeploymentTypeAndDefaultRealmAndVersion: end`\n );\n}\n\nasync function getLoggedInSubject(): Promise<string> {\n let subjectString = `user ${state.getUsername()}`;\n if (state.getUseBearerTokenForAmApis()) {\n const name = (\n await getManagedObject('svcacct', state.getServiceAccountId(), ['name'])\n ).data.name;\n subjectString = `service account ${name} [${state.getServiceAccountId()}]`;\n }\n return subjectString;\n}\n\n/**\n * Get tokens\n * @param {boolean} save true to save a connection profile upon successful authentication, false otherwise\n * @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(): Promise<boolean> {\n if (!state.getHost()) {\n printMessage(\n `No host specified and FRODO_HOST env variable not set!`,\n 'error'\n );\n return false;\n }\n try {\n // if username/password on cli are empty, try to read from connections.json\n if (\n state.getUsername() == null &&\n state.getPassword() == null &&\n !state.getServiceAccountId() &&\n !state.getServiceAccountJwk()\n ) {\n const conn = await getConnectionProfile();\n if (conn) {\n state.setHost(conn.tenant);\n state.setUsername(conn.username);\n state.setPassword(conn.password);\n state.setAuthenticationService(conn.authenticationService);\n state.setAuthenticationHeaderOverrides(\n conn.authenticationHeaderOverrides\n );\n state.setServiceAccountId(conn.svcacctId);\n state.setServiceAccountJwk(conn.svcacctJwk);\n } else {\n return false;\n }\n }\n // now that we have the full tenant URL we can lookup the cookie name\n state.setCookieName(await determineCookieName());\n\n // use service account to login?\n if (state.getServiceAccountId() && state.getServiceAccountJwk()) {\n debugMessage(\n `AuthenticateOps.getTokens: Authenticating with service account ${state.getServiceAccountId()}`\n );\n try {\n const token = await getAccessTokenForServiceAccount(\n state.getServiceAccountId(),\n state.getServiceAccountJwk()\n );\n state.setBearerToken(token);\n state.setUseBearerTokenForAmApis(true);\n await determineDeploymentTypeAndDefaultRealmAndVersion();\n } catch (saErr) {\n throw new Error(\n `Service account login error: ${\n saErr.response?.data?.error_description ||\n saErr.response?.data?.message\n }`\n );\n }\n }\n // use user account to login\n else if (state.getUsername() && state.getPassword()) {\n debugMessage(\n `AuthenticateOps.getTokens: Authenticating with user account ${state.getUsername()}`\n );\n const token = await authenticate(\n state.getUsername(),\n state.getPassword()\n );\n if (token) state.setCookieValue(token);\n await determineDeploymentTypeAndDefaultRealmAndVersion();\n if (\n state.getCookieValue() &&\n !state.getBearerToken() &&\n (state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY ||\n state.getDeploymentType() ===\n globalConfig.FORGEOPS_DEPLOYMENT_TYPE_KEY)\n ) {\n const accessToken = await getAccessTokenForUser();\n if (accessToken) state.setBearerToken(accessToken);\n }\n }\n // incomplete or no credentials\n else {\n printMessage(`Incomplete or no credentials!`, 'error');\n return false;\n }\n if (\n state.getCookieValue() ||\n (state.getUseBearerTokenForAmApis() && state.getBearerToken())\n ) {\n // https://github.com/rockcarver/frodo-cli/issues/102\n printMessage(\n `Connected to ${state.getHost()} [${\n state.getRealm() ? state.getRealm() : 'root'\n }] as ${await getLoggedInSubject()}`,\n 'info'\n );\n return true;\n }\n } catch (error) {\n // regular error\n printMessage(error.message, 'error');\n // axios error am api\n printMessage(error.response?.data?.message, 'error');\n // axios error am oauth2 api\n printMessage(error.response?.data?.error_description, 'error');\n // axios error data\n debugMessage(error.response?.data);\n // stack trace\n debugMessage(error.stack || new Error().stack);\n }\n return false;\n}\n"]}
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
import { JwkRsa } from './JoseOps';
|
|
2
|
+
import { ObjectSkeletonInterface } from '../api/ApiTypes';
|
|
3
|
+
export interface SecureConnectionProfileInterface {
|
|
4
|
+
tenant: string;
|
|
5
|
+
username?: string | null;
|
|
6
|
+
encodedPassword?: string | null;
|
|
7
|
+
logApiKey?: string | null;
|
|
8
|
+
encodedLogApiSecret?: string | null;
|
|
9
|
+
authenticationService?: string | null;
|
|
10
|
+
authenticationHeaderOverrides?: Record<string, string>;
|
|
11
|
+
svcacctId?: string | null;
|
|
12
|
+
encodedSvcacctJwk?: string | null;
|
|
13
|
+
svcacctName?: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ConnectionProfileInterface {
|
|
16
|
+
tenant: string;
|
|
17
|
+
username?: string | null;
|
|
18
|
+
password?: string | null;
|
|
19
|
+
logApiKey?: string | null;
|
|
20
|
+
logApiSecret?: string | null;
|
|
21
|
+
authenticationService?: string | null;
|
|
22
|
+
authenticationHeaderOverrides?: Record<string, string>;
|
|
23
|
+
svcacctId?: string | null;
|
|
24
|
+
svcacctJwk?: JwkRsa;
|
|
25
|
+
svcacctName?: string | null;
|
|
26
|
+
}
|
|
27
|
+
export interface ConnectionsFileInterface {
|
|
28
|
+
[key: string]: SecureConnectionProfileInterface;
|
|
29
|
+
}
|
|
1
30
|
/**
|
|
2
31
|
* Get connection profiles file name
|
|
3
32
|
* @returns {String} connection profiles file name
|
|
@@ -10,42 +39,41 @@ export declare function getConnectionProfilesPath(): string;
|
|
|
10
39
|
export declare function listConnectionProfiles(long?: boolean): void;
|
|
11
40
|
/**
|
|
12
41
|
* Initialize connection profiles
|
|
42
|
+
*
|
|
43
|
+
* This method is called from app.ts and runs before any of the message handlers are registered.
|
|
44
|
+
* Therefore none of the Console message functions will produce any output.
|
|
13
45
|
*/
|
|
14
|
-
export declare function initConnectionProfiles(): void
|
|
46
|
+
export declare function initConnectionProfiles(): Promise<void>;
|
|
15
47
|
/**
|
|
16
48
|
* Get connection profile by host
|
|
17
49
|
* @param {String} host host tenant host url or unique substring
|
|
18
50
|
* @returns {Object} connection profile or null
|
|
19
51
|
*/
|
|
20
|
-
export declare function getConnectionProfileByHost(host:
|
|
21
|
-
tenant: any;
|
|
22
|
-
username: any;
|
|
23
|
-
password: any;
|
|
24
|
-
key: any;
|
|
25
|
-
secret: any;
|
|
26
|
-
authenticationService: any;
|
|
27
|
-
authenticationHeaderOverrides: any;
|
|
28
|
-
}>;
|
|
52
|
+
export declare function getConnectionProfileByHost(host: string): Promise<ConnectionProfileInterface>;
|
|
29
53
|
/**
|
|
30
54
|
* Get connection profile
|
|
31
55
|
* @returns {Object} connection profile or null
|
|
32
56
|
*/
|
|
33
|
-
export declare function getConnectionProfile(): Promise<
|
|
34
|
-
tenant: any;
|
|
35
|
-
username: any;
|
|
36
|
-
password: any;
|
|
37
|
-
key: any;
|
|
38
|
-
secret: any;
|
|
39
|
-
authenticationService: any;
|
|
40
|
-
authenticationHeaderOverrides: any;
|
|
41
|
-
}>;
|
|
57
|
+
export declare function getConnectionProfile(): Promise<ConnectionProfileInterface>;
|
|
42
58
|
/**
|
|
43
59
|
* Save connection profile
|
|
60
|
+
* @param {string} host host url for new profiles or unique substring for existing profiles
|
|
61
|
+
* @returns {Promise<boolean>} true if the operation succeeded, false otherwise
|
|
44
62
|
*/
|
|
45
|
-
export declare function saveConnectionProfile(): Promise<
|
|
63
|
+
export declare function saveConnectionProfile(host: string): Promise<boolean>;
|
|
46
64
|
/**
|
|
47
65
|
* Delete connection profile
|
|
48
66
|
* @param {String} host host tenant host url or unique substring
|
|
49
67
|
*/
|
|
50
68
|
export declare function deleteConnectionProfile(host: any): void;
|
|
51
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Describe connection profile
|
|
71
|
+
* @param {string} host Host URL or unique substring
|
|
72
|
+
* @param {boolean} showSecrets Whether secrets should be shown in clear text or not
|
|
73
|
+
*/
|
|
74
|
+
export declare function describeConnectionProfile(host: string, showSecrets: boolean): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Create a new service account using auto-generated parameters
|
|
77
|
+
* @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object
|
|
78
|
+
*/
|
|
79
|
+
export declare function addNewServiceAccount(): Promise<ObjectSkeletonInterface>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/ConnectionProfileOps.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAoBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,UAAQ,QA2BlD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,SAkCrC;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,KAAA;;;;;;;;GA0CpD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB;;;;;;;;GAEzC;AAED;;GAEG;AACH,wBAAsB,qBAAqB,kBAoD1C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED,wBAAsB,yBAAyB,CAAC,IAAI,KAAA,EAAE,WAAW,KAAA,iBAyBhE","file":"ConnectionProfileOps.d.ts","sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport storage from '../storage/SessionStorage';\nimport DataProtection from './utils/DataProtection';\nimport { createObjectTable, createTable, printMessage } from './utils/Console';\nimport { FRODO_CONNECTION_PROFILES_PATH_KEY } from '../storage/StaticStorage';\nimport { profile } from 'console';\n\nconst dataProtection = new DataProtection();\n\nconst fileOptions = {\n indentation: 4,\n};\n\n/**\n * Get connection profiles file name\n * @returns {String} connection profiles file name\n */\nexport function getConnectionProfilesPath(): string {\n return (\n storage.session.getConnectionProfilesPath() ||\n process.env[FRODO_CONNECTION_PROFILES_PATH_KEY] ||\n `${os.homedir()}/.frodo/.frodorc`\n );\n}\n\n/**\n * Find connection profile\n * @param {Object} connectionProfiles connection profile object\n * @param {String} host tenant host url or unique substring\n * @returns {Object} connection profile object or null\n */\nfunction findConnectionProfile(connectionProfiles, host) {\n const profiles = [];\n for (const tenant in connectionProfiles) {\n if (tenant.includes(host)) {\n const foundProfile = connectionProfiles[tenant];\n foundProfile.tenant = tenant;\n profiles.push(foundProfile);\n }\n }\n return profiles;\n}\n\n/**\n * List connection profiles\n * @param {boolean} long Long list format with details\n */\nexport function listConnectionProfiles(long = false) {\n const filename = getConnectionProfilesPath();\n try {\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData = JSON.parse(data);\n if (long) {\n const table = createTable(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n }\n printMessage(\n 'Any unique substring of a saved host can be used as the value for host parameter in all commands',\n 'info'\n );\n } catch (e) {\n printMessage(`No connections found in ${filename} (${e.message})`, 'error');\n }\n}\n\n/**\n * Initialize connection profiles\n */\nexport function initConnectionProfiles() {\n // create connections.json file if it doesn't exist\n const filename = getConnectionProfilesPath();\n const folderName = path.dirname(filename);\n if (!fs.existsSync(folderName)) {\n fs.mkdirSync(folderName, { recursive: true });\n if (!fs.existsSync(filename)) {\n fs.writeFileSync(\n filename,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n }\n }\n // encrypt the password from clear text to aes-256-GCM\n else {\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData = JSON.parse(data);\n let convert = false;\n Object.keys(connectionsData).forEach(async (conn) => {\n if (connectionsData[conn].password) {\n convert = true;\n connectionsData[conn].encodedPassword = await dataProtection.encrypt(\n connectionsData[conn].password\n ); // Buffer.from(connectionsData[conn].password).toString('base64');\n delete connectionsData[conn].password;\n }\n });\n if (convert) {\n fs.writeFileSync(\n filename,\n JSON.stringify(connectionsData, null, fileOptions.indentation)\n );\n }\n }\n}\n\n/**\n * Get connection profile by host\n * @param {String} host host tenant host url or unique substring\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfileByHost(host) {\n try {\n const filename = getConnectionProfilesPath();\n const connectionsData = JSON.parse(fs.readFileSync(filename, 'utf8'));\n const profiles = findConnectionProfile(connectionsData, host);\n if (profiles.length == 0) {\n printMessage(\n `Profile for ${host} not found. Please specify credentials on command line`,\n 'error'\n );\n return null;\n }\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n }\n return {\n tenant: profiles[0].tenant,\n username: profiles[0].username ? profiles[0].username : null,\n password: profiles[0].encodedPassword\n ? await dataProtection.decrypt(profiles[0].encodedPassword)\n : null,\n key: profiles[0].logApiKey ? profiles[0].logApiKey : null,\n secret: profiles[0].logApiSecret ? profiles[0].logApiSecret : null,\n authenticationService: profiles[0].authenticationService\n ? profiles[0].authenticationService\n : null,\n authenticationHeaderOverrides: profiles[0].authenticationHeaderOverrides\n ? profiles[0].authenticationHeaderOverrides\n : {},\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile() {\n return getConnectionProfileByHost(storage.session.getTenant());\n}\n\n/**\n * Save connection profile\n */\nexport async function saveConnectionProfile() {\n const filename = getConnectionProfilesPath();\n printMessage(`Saving creds in ${filename}...`);\n let connectionsData = {};\n let existingData = {};\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n if (connectionsData[storage.session.getTenant()]) {\n existingData = connectionsData[storage.session.getTenant()];\n printMessage(\n `Updating connection profile ${storage.session.getTenant()}`\n );\n } else\n printMessage(`Adding connection profile ${storage.session.getTenant()}`);\n } catch (e) {\n printMessage(\n `Creating connection profiles file ${filename} with ${storage.session.getTenant()}`\n );\n }\n if (storage.session.getUsername())\n existingData['username'] = storage.session.getUsername();\n if (storage.session.getPassword())\n existingData['encodedPassword'] = await dataProtection.encrypt(\n storage.session.getPassword()\n );\n if (storage.session.getLogApiKey())\n existingData['logApiKey'] = storage.session.getLogApiKey();\n if (storage.session.getLogApiSecret())\n existingData['logApiSecret'] = storage.session.getLogApiSecret();\n\n // advanced settings\n if (storage.session.getAuthenticationService()) {\n existingData['authenticationService'] =\n storage.session.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n storage.session.getAuthenticationService(),\n 'info'\n );\n }\n if (storage.session.getAuthenticationHeaderOverrides()) {\n existingData['authenticationHeaderOverrides'] =\n storage.session.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(storage.session.getAuthenticationHeaderOverrides(), 'info');\n }\n\n connectionsData[storage.session.getTenant()] = existingData;\n\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfile(connectionsData, host);\n if (profiles.length == 1) {\n printMessage(`Deleting connection profile ${profiles[0].tenant}`);\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\nexport async function describeConnectionProfile(host, showSecrets) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n if (!showSecrets) {\n delete profile.password;\n delete profile.secret;\n }\n if (!profile.key) {\n delete profile.key;\n delete profile.secret;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n key: 'Log API Key',\n secret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/ConnectionProfileOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,MAAM,EAAE,MAAM,WAAW,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU1D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,gCAAgC,CAAC;CACjD;AAKD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAuBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,UAAQ,QA2BlD;AAeD;;;;;GAKG;AACH,wBAAsB,sBAAsB,kBAiD3C;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,0BAA0B,CAAC,CAgDrC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAEhF;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA4F1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,iBAgDrB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAqB7E","file":"ConnectionProfileOps.d.ts","sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport * as state from '../shared/State';\nimport DataProtection from './utils/DataProtection';\nimport {\n createObjectTable,\n createTable,\n debugMessage,\n printMessage,\n verboseMessage,\n} from './utils/Console';\nimport { FRODO_CONNECTION_PROFILES_PATH_KEY } from '../storage/StaticStorage';\nimport { createJwkRsa, createJwks, getJwkRsaPublic, JwkRsa } from './JoseOps';\nimport { createServiceAccount } from './ServiceAccountOps';\nimport { ObjectSkeletonInterface } from '../api/ApiTypes';\nimport { saveJsonToFile } from './utils/ExportImportUtils';\nimport { isValidUrl } from './utils/OpsUtils';\n\nconst crypto = new DataProtection();\n\nconst fileOptions = {\n indentation: 4,\n};\n\nexport interface SecureConnectionProfileInterface {\n tenant: string;\n username?: string | null;\n encodedPassword?: string | null;\n logApiKey?: string | null;\n encodedLogApiSecret?: string | null;\n authenticationService?: string | null;\n authenticationHeaderOverrides?: Record<string, string>;\n svcacctId?: string | null;\n encodedSvcacctJwk?: string | null;\n svcacctName?: string | null;\n}\n\nexport interface ConnectionProfileInterface {\n tenant: string;\n username?: string | null;\n password?: string | null;\n logApiKey?: string | null;\n logApiSecret?: string | null;\n authenticationService?: string | null;\n authenticationHeaderOverrides?: Record<string, string>;\n svcacctId?: string | null;\n svcacctJwk?: JwkRsa;\n svcacctName?: string | null;\n}\n\nexport interface ConnectionsFileInterface {\n [key: string]: SecureConnectionProfileInterface;\n}\n\nconst legacyProfileFilename = '.frodorc';\nconst newProfileFilename = 'Connections.json';\n\n/**\n * Get connection profiles file name\n * @returns {String} connection profiles file name\n */\nexport function getConnectionProfilesPath(): string {\n return (\n state.getConnectionProfilesPath() ||\n process.env[FRODO_CONNECTION_PROFILES_PATH_KEY] ||\n `${os.homedir()}/.frodo/${newProfileFilename}`\n );\n}\n\n/**\n * Find connection profiles\n * @param {ConnectionsFileInterface} connectionProfiles connection profile object\n * @param {string} host host url or unique substring\n * @returns {SecureConnectionProfileInterface[]} Array of connection profiles\n */\nfunction findConnectionProfiles(\n connectionProfiles: ConnectionsFileInterface,\n host: string\n): SecureConnectionProfileInterface[] {\n const profiles: SecureConnectionProfileInterface[] = [];\n for (const tenant in connectionProfiles) {\n if (tenant.includes(host)) {\n const foundProfile = { ...connectionProfiles[tenant] };\n foundProfile.tenant = tenant;\n profiles.push(foundProfile);\n }\n }\n return profiles;\n}\n\n/**\n * List connection profiles\n * @param {boolean} long Long list format with details\n */\nexport function listConnectionProfiles(long = false) {\n const filename = getConnectionProfilesPath();\n try {\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData = JSON.parse(data);\n if (long) {\n const table = createTable(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n }\n printMessage(\n 'Any unique substring of a saved host can be used as the value for host parameter in all commands',\n 'info'\n );\n } catch (e) {\n printMessage(`No connections found in ${filename} (${e.message})`, 'error');\n }\n}\n\n/**\n * Migrate from .frodorc to Connections.json\n */\nfunction migrateFromLegacyProfile() {\n const legacyPath = `${os.homedir()}/.frodo/${legacyProfileFilename}`;\n if (fs.existsSync(legacyPath)) {\n fs.copyFileSync(legacyPath, `${os.homedir()}/.frodo/${newProfileFilename}`);\n // for now, just add a \"deprecated\" suffix. May delete the old file\n // in a future release\n fs.renameSync(legacyPath, `${legacyPath}.deprecated`);\n }\n}\n\n/**\n * Initialize connection profiles\n *\n * This method is called from app.ts and runs before any of the message handlers are registered.\n * Therefore none of the Console message functions will produce any output.\n */\nexport async function initConnectionProfiles() {\n // create connections.json file if it doesn't exist\n const filename = getConnectionProfilesPath();\n const folderName = path.dirname(filename);\n if (!fs.existsSync(folderName)) {\n fs.mkdirSync(folderName, { recursive: true });\n if (!fs.existsSync(filename)) {\n fs.writeFileSync(\n filename,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n }\n }\n // encrypt the password and logApiSecret from clear text to aes-256-GCM\n else {\n migrateFromLegacyProfile();\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData: ConnectionsFileInterface = JSON.parse(data);\n let convert = false;\n for (const conn of Object.keys(connectionsData)) {\n if (connectionsData[conn]['password']) {\n convert = true;\n connectionsData[conn].encodedPassword = await crypto.encrypt(\n connectionsData[conn]['password']\n );\n delete connectionsData[conn]['password'];\n }\n if (connectionsData[conn]['logApiSecret']) {\n convert = true;\n connectionsData[conn].encodedLogApiSecret = await crypto.encrypt(\n connectionsData[conn]['logApiSecret']\n );\n delete connectionsData[conn]['logApiSecret'];\n }\n if (connectionsData[conn]['svcacctJwk']) {\n convert = true;\n connectionsData[conn].encodedSvcacctJwk = await crypto.encrypt(\n connectionsData[conn]['svcacctJwk']\n );\n delete connectionsData[conn]['svcacctJwk'];\n }\n }\n if (convert) {\n fs.writeFileSync(\n filename,\n JSON.stringify(connectionsData, null, fileOptions.indentation)\n );\n }\n }\n}\n\n/**\n * Get connection profile by host\n * @param {String} host host tenant host url or unique substring\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfileByHost(\n host: string\n): Promise<ConnectionProfileInterface> {\n try {\n const filename = getConnectionProfilesPath();\n const connectionsData = JSON.parse(fs.readFileSync(filename, 'utf8'));\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 0) {\n printMessage(\n `Profile for ${host} not found. Please specify credentials on command line`,\n 'error'\n );\n return null;\n }\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n }\n return {\n tenant: profiles[0].tenant,\n username: profiles[0].username ? profiles[0].username : null,\n password: profiles[0].encodedPassword\n ? await crypto.decrypt(profiles[0].encodedPassword)\n : null,\n logApiKey: profiles[0].logApiKey ? profiles[0].logApiKey : null,\n logApiSecret: profiles[0].encodedLogApiSecret\n ? await crypto.decrypt(profiles[0].encodedLogApiSecret)\n : null,\n authenticationService: profiles[0].authenticationService\n ? profiles[0].authenticationService\n : null,\n authenticationHeaderOverrides: profiles[0].authenticationHeaderOverrides\n ? profiles[0].authenticationHeaderOverrides\n : {},\n svcacctId: profiles[0].svcacctId ? profiles[0].svcacctId : null,\n svcacctJwk: profiles[0].encodedSvcacctJwk\n ? await crypto.decrypt(profiles[0].encodedSvcacctJwk)\n : null,\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile(): Promise<ConnectionProfileInterface> {\n return getConnectionProfileByHost(state.getHost());\n}\n\n/**\n * Save connection profile\n * @param {string} host host url for new profiles or unique substring for existing profiles\n * @returns {Promise<boolean>} true if the operation succeeded, false otherwise\n */\nexport async function saveConnectionProfile(host: string): Promise<boolean> {\n const filename = getConnectionProfilesPath();\n verboseMessage(`Saving connection profile in ${filename}`);\n let profiles: ConnectionsFileInterface = {};\n let profile: SecureConnectionProfileInterface = { tenant: '' };\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n profiles = JSON.parse(data);\n\n // find tenant\n const found = findConnectionProfiles(profiles, host);\n\n // replace tenant in session with real tenant url if necessary\n if (found.length === 1) {\n profile = found[0];\n state.setHost(profile.tenant);\n verboseMessage(`Existing profile: ${profile.tenant}`);\n }\n\n // connection profile not found, validate host is a real URL\n if (found.length === 0) {\n if (isValidUrl(host)) {\n state.setHost(host);\n verboseMessage(`New profile: ${host}`);\n } else {\n printMessage(\n `No existing profile found matching '${host}'. Provide a valid URL as the host argument to create a new profile.`,\n 'error'\n );\n return false;\n }\n }\n } catch (error) {\n verboseMessage(`New profiles file ${filename} with new profile ${host}`);\n }\n\n // user account\n if (state.getUsername()) profile.username = state.getUsername();\n if (state.getPassword())\n profile.encodedPassword = await crypto.encrypt(state.getPassword());\n\n // log API\n if (state.getLogApiKey()) profile.logApiKey = state.getLogApiKey();\n if (state.getLogApiSecret())\n profile.encodedLogApiSecret = await crypto.encrypt(state.getLogApiSecret());\n\n // service account\n if (state.getServiceAccountId())\n profile.svcacctId = state.getServiceAccountId();\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n\n // advanced settings\n if (state.getAuthenticationService()) {\n profile.authenticationService = state.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n state.getAuthenticationService(),\n 'info'\n );\n }\n if (\n state.getAuthenticationHeaderOverrides() &&\n Object.entries(state.getAuthenticationHeaderOverrides()).length\n ) {\n profile.authenticationHeaderOverrides =\n state.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(state.getAuthenticationHeaderOverrides(), 'info');\n }\n\n // remove the helper key 'tenant'\n delete profile.tenant;\n\n // update profiles\n profiles[state.getHost()] = profile;\n\n // sort profiles\n const orderedProfiles = Object.keys(profiles)\n .sort()\n .reduce((obj, key) => {\n obj[key] = profiles[key];\n return obj;\n }, {});\n\n // save profiles\n saveJsonToFile(orderedProfiles, filename, false);\n verboseMessage(`Saved connection profile ${state.getHost()} in ${filename}`);\n return true;\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData: ConnectionsFileInterface = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 1) {\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n printMessage(`Deleted connection profile ${profiles[0].tenant}`);\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\n/**\n * Describe connection profile\n * @param {string} host Host URL or unique substring\n * @param {boolean} showSecrets Whether secrets should be shown in clear text or not\n */\nexport async function describeConnectionProfile(\n host: string,\n showSecrets: boolean\n) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n const present = '[present]';\n const jwk = profile.svcacctJwk;\n if (!showSecrets) {\n if (profile.password) profile.password = present;\n if (profile.logApiSecret) profile.logApiSecret = present;\n if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;\n }\n if (!profile.username) {\n delete profile.username;\n delete profile.password;\n }\n if (!profile.logApiKey) {\n delete profile.logApiKey;\n delete profile.logApiSecret;\n }\n if (!profile.svcacctId) {\n delete profile.svcacctId;\n delete profile.svcacctJwk;\n }\n if (showSecrets && jwk) {\n (profile as unknown)['svcacctJwk'] = 'see below';\n }\n if (!profile.authenticationService) {\n delete profile.authenticationService;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n logApiKey: 'Log API Key',\n logApiSecret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n svcacctId: 'Service Account Id',\n svcacctJwk: 'Service Account JWK',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n if (showSecrets && jwk) {\n printMessage(jwk, 'data');\n }\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n\n/**\n * Create a new service account using auto-generated parameters\n * @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object\n */\nexport async function addNewServiceAccount(): Promise<ObjectSkeletonInterface> {\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: start`);\n const name = `Frodo-SA-${new Date().getTime()}`;\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: name=${name}...`);\n const description = `${state.getUsername()}'s Frodo Service Account`;\n const scope = ['fr:am:*', 'fr:idm:*', 'fr:idc:esv:*'];\n const jwkPrivate = await createJwkRsa();\n const jwkPublic = await getJwkRsaPublic(jwkPrivate);\n const jwks = createJwks(jwkPublic);\n const sa = await createServiceAccount(\n name,\n description,\n 'Active',\n scope,\n jwks\n );\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: id=${sa._id}`);\n state.setServiceAccountId(sa._id);\n state.setServiceAccountJwk(jwkPrivate);\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: end`);\n return sa;\n}\n"]}
|
|
@@ -1,41 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {boolean} long Long list format with details
|
|
4
|
-
* @return {Promise<unknown[]>} a promise that resolves to an array of email template objects
|
|
2
|
+
* Email template type key used to build the IDM id: 'emailTemplate/<id>'
|
|
5
3
|
*/
|
|
6
|
-
export declare
|
|
4
|
+
export declare const EMAIL_TEMPLATE_TYPE = "emailTemplate";
|
|
7
5
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
10
|
-
* @param {String} file optional filename
|
|
6
|
+
* Get all email templates
|
|
7
|
+
* @returns {Promise} a promise that resolves to an array of email template objects
|
|
11
8
|
*/
|
|
12
|
-
export declare function
|
|
9
|
+
export declare function getEmailTemplates(): Promise<any>;
|
|
13
10
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param {
|
|
11
|
+
* Get email template
|
|
12
|
+
* @param {string} templateId id/name of the email template without the type prefix
|
|
13
|
+
* @returns {Promise} a promise that resolves an email template object
|
|
16
14
|
*/
|
|
17
|
-
export declare function
|
|
15
|
+
export declare function getEmailTemplate(templateId: any): Promise<any>;
|
|
18
16
|
/**
|
|
19
|
-
*
|
|
17
|
+
* Put email template
|
|
18
|
+
* @param {string} templateId id/name of the email template without the type prefix
|
|
19
|
+
* @param {Object} templateData email template object
|
|
20
|
+
* @returns {Promise} a promise that resolves to an email template object
|
|
20
21
|
*/
|
|
21
|
-
export declare function
|
|
22
|
-
/**
|
|
23
|
-
* Import email template by id from file
|
|
24
|
-
* @param {String} templateId email template id
|
|
25
|
-
* @param {String} file optional filename
|
|
26
|
-
*/
|
|
27
|
-
export declare function importEmailTemplateFromFile(templateId: any, file: any): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Import all email templates from file
|
|
30
|
-
* @param {String} file optional filename
|
|
31
|
-
*/
|
|
32
|
-
export declare function importEmailTemplatesFromFile(file: any): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Import all email templates from separate files
|
|
35
|
-
*/
|
|
36
|
-
export declare function importEmailTemplatesFromFiles(): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Import first email template from file
|
|
39
|
-
* @param {String} file optional filename
|
|
40
|
-
*/
|
|
41
|
-
export declare function importFirstEmailTemplateFromFile(file: any): Promise<void>;
|
|
22
|
+
export declare function putEmailTemplate(templateId: any, templateData: any): Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/EmailTemplateOps.ts"],"names":[],"mappings":"AAuCA;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,UAAQ,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAmDzE;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,UAAU,KAAA,EAAE,IAAI,KAAA,iBAmB/D;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,KAAA,iBAuBpD;AAED;;GAEG;AACH,wBAAsB,2BAA2B,kBAkBhD;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAAC,UAAU,KAAA,EAAE,IAAI,KAAA,iBAiCjE;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,KAAA,iBA8BtD;AAED;;GAEG;AACH,wBAAsB,6BAA6B,kBAyClD;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CAAC,IAAI,KAAA,iBA2B1D","file":"EmailTemplateOps.d.ts","sourcesContent":["import fs from 'fs';\nimport {\n EMAIL_TEMPLATE_TYPE,\n getEmailTemplate,\n getEmailTemplates,\n putEmailTemplate,\n} from '../api/EmailTemplateApi';\nimport {\n createProgressIndicator,\n createTable,\n printMessage,\n stopProgressIndicator,\n updateProgressIndicator,\n} from './utils/Console';\nimport {\n getTypedFilename,\n saveJsonToFile,\n validateImport,\n} from './utils/ExportImportUtils';\nimport wordwrap from './utils/Wordwrap';\n\n/**\n * Regex to remove email template type from id\n */\nconst regexEmailTemplateType = new RegExp(`${EMAIL_TEMPLATE_TYPE}/`, 'g');\n\n/**\n * Maintain the file type centrally\n */\nconst EMAIL_TEMPLATE_FILE_TYPE = 'template.email';\n\n// use a function vs a template variable to avoid problems in loops\nfunction getFileDataTemplate() {\n return {\n meta: {},\n emailTemplate: {},\n };\n}\n\n/**\n * List email templates\n * @param {boolean} long Long list format with details\n * @return {Promise<unknown[]>} a promise that resolves to an array of email template objects\n */\nexport async function listEmailTemplates(long = false): Promise<unknown[]> {\n let emailTemplates = [];\n try {\n emailTemplates = (await getEmailTemplates()).result;\n } catch (error) {\n printMessage(`Error retrieving email templates: ${error.message}`, 'error');\n }\n emailTemplates.sort((a, b) => a._id.localeCompare(b._id));\n if (!long) {\n for (const [, emailTemplate] of emailTemplates.entries()) {\n printMessage(\n `${emailTemplate._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '')}`,\n 'data'\n );\n }\n } else {\n const table = createTable([\n 'Id'['brightCyan'],\n 'Name'['brightCyan'],\n 'Status'['brightCyan'],\n 'Locale(s)'['brightCyan'],\n 'From'['brightCyan'],\n 'Subject'['brightCyan'],\n ]);\n for (const emailTemplate of emailTemplates) {\n table.push([\n // Id\n `${emailTemplate._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '')}`,\n // Name\n `${emailTemplate.displayName ? emailTemplate.displayName : ''}`,\n // Status\n emailTemplate.enabled === false\n ? 'disabled'['brightRed']\n : 'enabled'['brightGreen'],\n // Locale(s)\n `${emailTemplate.defaultLocale}${\n Object.keys(emailTemplate.subject).length > 1\n ? ` (${Object.keys(emailTemplate.subject)\n .filter((locale) => locale !== emailTemplate.defaultLocale)\n .join(',')})`\n : ''\n }`,\n // From\n `${emailTemplate.from ? emailTemplate.from : ''}`,\n // Subject\n wordwrap(emailTemplate.subject[emailTemplate.defaultLocale], 40),\n ]);\n }\n printMessage(table.toString(), 'data');\n }\n return emailTemplates;\n}\n\n/**\n * Export a single email template to file\n * @param {String} templateId email template id\n * @param {String} file optional filename\n */\nexport async function exportEmailTemplateToFile(templateId, file) {\n let fileName = file;\n if (!fileName) {\n fileName = getTypedFilename(templateId, EMAIL_TEMPLATE_FILE_TYPE);\n }\n createProgressIndicator(1, `Exporting ${templateId}`);\n try {\n const templateData = await getEmailTemplate(templateId);\n updateProgressIndicator(`Writing file ${fileName}`);\n const fileData = getFileDataTemplate();\n fileData.emailTemplate[templateId] = templateData;\n saveJsonToFile(fileData, fileName);\n stopProgressIndicator(\n `Exported ${templateId.brightCyan} to ${fileName.brightCyan}.`\n );\n } catch (err) {\n stopProgressIndicator(`${err}`);\n printMessage(err, 'error');\n }\n}\n\n/**\n * Export all email templates to file\n * @param {String} file optional filename\n */\nexport async function exportEmailTemplatesToFile(file) {\n let fileName = file;\n if (!fileName) {\n fileName = getTypedFilename(`allEmailTemplates`, EMAIL_TEMPLATE_FILE_TYPE);\n }\n try {\n const fileData = getFileDataTemplate();\n const response = await getEmailTemplates();\n const templates = response.result;\n createProgressIndicator(response.resultCount, 'Exporting email templates');\n for (const template of templates) {\n const templateId = template._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '');\n updateProgressIndicator(`Exporting ${templateId}`);\n fileData.emailTemplate[templateId] = template;\n }\n saveJsonToFile(fileData, fileName);\n stopProgressIndicator(\n `${response.resultCount} templates exported to ${fileName}.`\n );\n } catch (err) {\n stopProgressIndicator(`${err}`);\n printMessage(err, 'error');\n }\n}\n\n/**\n * Export all email templates to separate files\n */\nexport async function exportEmailTemplatesToFiles() {\n try {\n const response = await getEmailTemplates();\n const templates = response.result;\n createProgressIndicator(response.resultCount, 'Exporting email templates');\n for (const template of templates) {\n const templateId = template._id.replace(`${EMAIL_TEMPLATE_TYPE}/`, '');\n const fileName = getTypedFilename(templateId, EMAIL_TEMPLATE_FILE_TYPE);\n const fileData = getFileDataTemplate();\n updateProgressIndicator(`Exporting ${templateId}`);\n fileData.emailTemplate[templateId] = template;\n saveJsonToFile(fileData, fileName);\n }\n stopProgressIndicator(`${response.resultCount} templates exported.`);\n } catch (err) {\n stopProgressIndicator(`${err}`);\n printMessage(err, 'error');\n }\n}\n\n/**\n * Import email template by id from file\n * @param {String} templateId email template id\n * @param {String} file optional filename\n */\nexport async function importEmailTemplateFromFile(templateId, file) {\n // eslint-disable-next-line no-param-reassign\n templateId = templateId.replaceAll(`${EMAIL_TEMPLATE_TYPE}/`, '');\n fs.readFile(file, 'utf8', async (err, data) => {\n if (err) throw err;\n const fileData = JSON.parse(data);\n if (validateImport(fileData.meta)) {\n createProgressIndicator(1, `Importing ${templateId}`);\n if (fileData.emailTemplate[templateId]) {\n try {\n await putEmailTemplate(\n templateId,\n fileData.emailTemplate[templateId]\n );\n updateProgressIndicator(`Importing ${templateId}`);\n stopProgressIndicator(`Imported ${templateId}`);\n } catch (putEmailTemplateError) {\n stopProgressIndicator(`${putEmailTemplateError}`);\n printMessage(putEmailTemplateError, 'error');\n }\n } else {\n stopProgressIndicator(\n `Email template ${templateId.brightCyan} not found in ${file.brightCyan}!`\n );\n printMessage(\n `Email template ${templateId.brightCyan} not found in ${file.brightCyan}!`,\n 'error'\n );\n }\n } else {\n printMessage('Import validation failed...', 'error');\n }\n });\n}\n\n/**\n * Import all email templates from file\n * @param {String} file optional filename\n */\nexport async function importEmailTemplatesFromFile(file) {\n fs.readFile(file, 'utf8', async (err, data) => {\n if (err) throw err;\n const fileData = JSON.parse(data);\n if (validateImport(fileData.meta)) {\n createProgressIndicator(\n Object.keys(fileData.emailTemplate).length,\n `Importing email templates`\n );\n for (const id in fileData.emailTemplate) {\n if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {\n const templateId = id.replace(regexEmailTemplateType, '');\n try {\n // eslint-disable-next-line no-await-in-loop\n await putEmailTemplate(\n templateId,\n fileData.emailTemplate[templateId]\n );\n updateProgressIndicator(`Imported ${templateId}`);\n } catch (putEmailTemplateError) {\n printMessage(`\\nError importing ${templateId}`, 'error');\n printMessage(putEmailTemplateError.response.data, 'error');\n }\n }\n }\n stopProgressIndicator(`Done.`);\n } else {\n printMessage('Import validation failed...', 'error');\n }\n });\n}\n\n/**\n * Import all email templates from separate files\n */\nexport async function importEmailTemplatesFromFiles() {\n const names = fs.readdirSync('.');\n const jsonFiles = names.filter((name) =>\n name.toLowerCase().endsWith(`${EMAIL_TEMPLATE_FILE_TYPE}.json`)\n );\n createProgressIndicator(jsonFiles.length, 'Importing email templates...');\n let total = 0;\n let totalErrors = 0;\n for (const file of jsonFiles) {\n const data = fs.readFileSync(file, 'utf8');\n const fileData = JSON.parse(data);\n if (validateImport(fileData.meta)) {\n total += Object.keys(fileData.emailTemplate).length;\n let errors = 0;\n for (const id in fileData.emailTemplate) {\n if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {\n const templateId = id.replace(regexEmailTemplateType, '');\n try {\n // eslint-disable-next-line no-await-in-loop\n await putEmailTemplate(\n templateId,\n fileData.emailTemplate[templateId]\n );\n } catch (putEmailTemplateError) {\n errors += 1;\n printMessage(`\\nError importing ${templateId}`, 'error');\n printMessage(putEmailTemplateError.response.data, 'error');\n }\n }\n }\n totalErrors += errors;\n updateProgressIndicator(`Imported ${file}`);\n } else {\n printMessage(`Validation of ${file} failed!`, 'error');\n }\n }\n stopProgressIndicator(\n `Imported ${total - totalErrors} of ${total} email template(s) from ${\n jsonFiles.length\n } file(s).`\n );\n}\n\n/**\n * Import first email template from file\n * @param {String} file optional filename\n */\nexport async function importFirstEmailTemplateFromFile(file) {\n fs.readFile(file, 'utf8', async (err, data) => {\n if (err) throw err;\n const fileData = JSON.parse(data);\n if (validateImport(fileData.meta)) {\n createProgressIndicator(1, `Importing first email template`);\n for (const id in fileData.emailTemplate) {\n if ({}.hasOwnProperty.call(fileData.emailTemplate, id)) {\n try {\n await putEmailTemplate(\n id.replace(regexEmailTemplateType, ''),\n fileData.emailTemplate[id]\n );\n updateProgressIndicator(`Imported ${id}`);\n stopProgressIndicator(`Imported ${id}`);\n } catch (putEmailTemplateError) {\n stopProgressIndicator(`Error importing email template ${id}`);\n printMessage(`\\nError importing email template ${id}`, 'error');\n printMessage(putEmailTemplateError.response.data, 'error');\n }\n break;\n }\n }\n } else {\n printMessage('Import validation failed...', 'error');\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/EmailTemplateOps.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD;;;GAGG;AACH,wBAAsB,iBAAiB,iBAEtC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,KAAA,gBAEhD;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,KAAA,EAAE,YAAY,KAAA,gBAE9D","file":"EmailTemplateOps.d.ts","sourcesContent":["import {\n getConfigEntitiesByType,\n getConfigEntity,\n putConfigEntity,\n} from '../api/IdmConfigApi';\n\n/**\n * Email template type key used to build the IDM id: 'emailTemplate/<id>'\n */\nexport const EMAIL_TEMPLATE_TYPE = 'emailTemplate';\n\n/**\n * Get all email templates\n * @returns {Promise} a promise that resolves to an array of email template objects\n */\nexport async function getEmailTemplates() {\n return getConfigEntitiesByType(EMAIL_TEMPLATE_TYPE);\n}\n\n/**\n * Get email template\n * @param {string} templateId id/name of the email template without the type prefix\n * @returns {Promise} a promise that resolves an email template object\n */\nexport async function getEmailTemplate(templateId) {\n return getConfigEntity(`${EMAIL_TEMPLATE_TYPE}/${templateId}`);\n}\n\n/**\n * Put email template\n * @param {string} templateId id/name of the email template without the type prefix\n * @param {Object} templateData email template object\n * @returns {Promise} a promise that resolves to an email template object\n */\nexport async function putEmailTemplate(templateId, templateData) {\n return putConfigEntity(`${EMAIL_TEMPLATE_TYPE}/${templateId}`, templateData);\n}\n"]}
|
package/types/ops/IdmOps.d.ts
CHANGED
|
@@ -1,47 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Get all IDM config entities
|
|
3
|
+
* @returns {Promise} a promise that resolves to all IDM config entities
|
|
3
4
|
*/
|
|
4
|
-
export declare function
|
|
5
|
+
export declare function getAllConfigEntities(): Promise<any>;
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param {String}
|
|
8
|
-
* @
|
|
7
|
+
* Get IDM config entities by type
|
|
8
|
+
* @param {String} type the desired type of config entity
|
|
9
|
+
* @returns {Promise} a promise that resolves to an object containing all IDM config entities of the desired type
|
|
9
10
|
*/
|
|
10
|
-
export declare function
|
|
11
|
+
export declare function getConfigEntitiesByType(type: any): Promise<any>;
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param {
|
|
13
|
+
* Get an IDM config entity
|
|
14
|
+
* @param {string} entityId the desired config entity
|
|
15
|
+
* @returns {Promise<unknown>} a promise that resolves to an IDM config entity
|
|
14
16
|
*/
|
|
15
|
-
export declare function
|
|
17
|
+
export declare function getConfigEntity(entityId: any): Promise<any>;
|
|
16
18
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
19
|
+
* Put IDM config entity
|
|
20
|
+
* @param {string} entityId config entity id
|
|
21
|
+
* @param {string} entityData config entity object
|
|
22
|
+
* @returns {Promise<unknown>} a promise that resolves to an IDM config entity
|
|
21
23
|
*/
|
|
22
|
-
export declare function
|
|
24
|
+
export declare function putConfigEntity(entityId: string, entityData: string | object): Promise<any>;
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param
|
|
26
|
-
* @param
|
|
26
|
+
* Query managed objects
|
|
27
|
+
* @param {String} type managed object type
|
|
28
|
+
* @param {[String]} fields fields to retrieve
|
|
29
|
+
* @param {String} pageCookie paged results cookie
|
|
30
|
+
* @returns {Promise<{result: any[]; resultCount: number; pagedResultsCookie: any; totalPagedResultsPolicy: string; totalPagedResults: number; remainingPagedResults: number;}>} a promise that resolves to managed objects of the desired type
|
|
27
31
|
*/
|
|
28
|
-
export declare function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* Import all IDM configuration objects
|
|
37
|
-
* @param baseDirectory import directory
|
|
38
|
-
* @param entitiesFile JSON file that specifies the config entities to export/import
|
|
39
|
-
* @param envFile File that defines environment specific variables for replacement during configuration export/import
|
|
40
|
-
* @param validate validate script hooks
|
|
41
|
-
*/
|
|
42
|
-
export declare function importAllConfigEntities(baseDirectory: string, entitiesFile: string, envFile: string, validate?: boolean): Promise<void>;
|
|
43
|
-
/**
|
|
44
|
-
* Count number of managed objects of a given type
|
|
45
|
-
* @param {String} type managed object type, e.g. alpha_user
|
|
46
|
-
*/
|
|
47
|
-
export declare function countManagedObjects(type: any): Promise<void>;
|
|
32
|
+
export declare function queryAllManagedObjectsByType(type: any, fields: any, pageCookie: any): Promise<{
|
|
33
|
+
result: unknown[];
|
|
34
|
+
resultCount: number;
|
|
35
|
+
pagedResultsCookie: string;
|
|
36
|
+
totalPagedResultsPolicy: string;
|
|
37
|
+
totalPagedResults: number;
|
|
38
|
+
remainingPagedResults: number;
|
|
39
|
+
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops/IdmOps.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,wBAAsB,qBAAqB,kBAgB1C;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,KAAA,EAAE,IAAI,KAAA,iBAYhD;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,SAAS,KAAA,iBAmEzD;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,SAAS,KAAA,EACT,YAAY,KAAA,EACZ,OAAO,KAAA,iBAqER;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,OAAO,iBAqBnB;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,OAAO,iBAgEnB;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,OAAO,iBAyEnB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,KAAA,iBAwB7C","file":"IdmOps.d.ts","sourcesContent":["/* eslint-disable no-await-in-loop */\nimport fs from 'fs';\nimport fse from 'fs-extra';\nimport path from 'path';\nimport propertiesReader from 'properties-reader';\nimport replaceall from 'replaceall';\nimport {\n getAllConfigEntities,\n getConfigEntity,\n putConfigEntity,\n queryAllManagedObjectsByType,\n} from '../api/IdmConfigApi';\nimport {\n createProgressIndicator,\n printMessage,\n stopProgressIndicator,\n} from './utils/Console';\nimport { getTypedFilename } from './utils/ExportImportUtils';\nimport { readFiles, unSubstituteEnvParams } from './utils/OpsUtils';\nimport { validateScriptHooks } from './utils/ValidationUtils';\n\n/**\n * List all IDM configuration objects\n */\nexport async function listAllConfigEntities() {\n let configEntities = [];\n try {\n configEntities = await getAllConfigEntities();\n } catch (getAllConfigEntitiesError) {\n printMessage(getAllConfigEntitiesError, 'error');\n printMessage(\n `Error getting config entities: ${getAllConfigEntitiesError}`,\n 'error'\n );\n }\n if ('configurations' in configEntities) {\n configEntities['configurations'].forEach((configEntity) => {\n printMessage(`${configEntity._id}`, 'data');\n });\n }\n}\n\n/**\n * Export an IDM configuration object.\n * @param {String} id the desired configuration object\n * @param {String} file optional export file\n */\nexport async function exportConfigEntity(id, file) {\n let fileName = file;\n if (!fileName) {\n fileName = getTypedFilename(`${id}`, 'idm');\n }\n const configEntity = await getConfigEntity(id);\n fs.writeFile(fileName, JSON.stringify(configEntity, null, 2), (err) => {\n if (err) {\n return printMessage(`ERROR - can't save ${id} export to file`, 'error');\n }\n return '';\n });\n}\n\n/**\n * Export all IDM configuration objects into separate JSON files in a directory specified by <directory>\n * @param {String} directory export directory\n */\nexport async function exportAllRawConfigEntities(directory) {\n let configEntities = [];\n try {\n configEntities = await getAllConfigEntities();\n } catch (getAllConfigEntitiesError) {\n printMessage(getAllConfigEntitiesError, 'error');\n printMessage(\n `Error getting config entities: ${getAllConfigEntitiesError}`,\n 'error'\n );\n }\n if ('configurations' in configEntities) {\n if (!fs.existsSync(directory)) {\n fs.mkdirSync(directory);\n }\n createProgressIndicator(\n undefined,\n 'Exporting config objects...',\n 'indeterminate'\n );\n const entityPromises = [];\n configEntities['configurations'].forEach((x) => {\n entityPromises.push(\n getConfigEntity(x._id).catch((getConfigEntityError) => {\n if (\n !(\n getConfigEntityError.response?.status === 403 &&\n getConfigEntityError.response?.data?.message ===\n 'This operation is not available in ForgeRock Identity Cloud.'\n ) &&\n // https://bugster.forgerock.org/jira/browse/OPENIDM-18270\n !(\n getConfigEntityError.response?.status === 404 &&\n getConfigEntityError.response?.data?.message ===\n 'No configuration exists for id org.apache.felix.fileinstall/openidm'\n )\n ) {\n printMessage(getConfigEntityError.response?.data, 'error');\n printMessage(\n `Error getting config entity ${x._id}: ${getConfigEntityError}`,\n 'error'\n );\n }\n })\n );\n });\n Promise.all(entityPromises).then((result) => {\n result.forEach((item) => {\n if (item != null) {\n fse.outputFile(\n `${directory}/${item._id}.json`,\n JSON.stringify(item, null, 2),\n // eslint-disable-next-line consistent-return\n (err) => {\n if (err) {\n return printMessage(\n `ERROR - can't save config ${item._id} to file - ${err}`,\n 'error'\n );\n }\n }\n );\n }\n });\n stopProgressIndicator('Exported config objects.', 'success');\n });\n }\n}\n\n/**\n * Export all IDM configuration objects\n * @param {String} directory export directory\n * @param {String} entitiesFile JSON file that specifies the config entities to export/import\n * @param {String} envFile File that defines environment specific variables for replacement during configuration export/import\n */\nexport async function exportAllConfigEntities(\n directory,\n entitiesFile,\n envFile\n) {\n let entriesToExport = [];\n // read list of entities to export\n fs.readFile(entitiesFile, 'utf8', async (err, data) => {\n if (err) throw err;\n const entriesData = JSON.parse(data);\n entriesToExport = entriesData.idm;\n // console.log(`entriesToExport ${entriesToExport}`);\n\n // read list of configs to parameterize for environment specific values\n const envParams = propertiesReader(envFile);\n\n let configEntities = [];\n try {\n configEntities = await getAllConfigEntities();\n } catch (getAllConfigEntitiesError) {\n printMessage(getAllConfigEntitiesError, 'error');\n printMessage(\n `Error getting config entities: ${getAllConfigEntitiesError}`,\n 'error'\n );\n }\n if ('configurations' in configEntities) {\n // create export directory if not exist\n if (!fs.existsSync(directory)) {\n fs.mkdirSync(directory);\n }\n createProgressIndicator(\n undefined,\n 'Exporting config objects...',\n 'indeterminate'\n );\n const entityPromises = [];\n configEntities['configurations'].forEach((x) => {\n if (entriesToExport.includes(x._id)) {\n entityPromises.push(getConfigEntity(x._id));\n }\n });\n Promise.all(entityPromises).then((result) => {\n result.forEach((item) => {\n if (item != null) {\n let configEntityString = JSON.stringify(item, null, 2);\n envParams.each((key, value) => {\n configEntityString = replaceall(\n value,\n `\\${${key}}`,\n configEntityString\n );\n });\n fse.outputFile(\n `${directory}/${item._id}.json`,\n configEntityString,\n // eslint-disable-next-line consistent-return\n (error) => {\n if (err) {\n return printMessage(\n `ERROR - can't save config ${item._id} to file - ${error}`,\n 'error'\n );\n }\n }\n );\n }\n });\n stopProgressIndicator(null, 'success');\n });\n }\n });\n}\n\n/**\n * Import an IDM configuration object.\n * @param entityId the configuration object to import\n * @param file optional file to import\n */\nexport async function importConfigEntity(\n entityId: string,\n file?: string,\n validate?: boolean\n) {\n if (!file) {\n file = getTypedFilename(entityId, 'idm');\n }\n\n const entityData = fs.readFileSync(path.resolve(process.cwd(), file), 'utf8');\n\n const jsObject = JSON.parse(entityData);\n const isValid = validateScriptHooks(jsObject);\n if (validate && !isValid) {\n printMessage('Invalid IDM configuration object', 'error');\n return;\n }\n\n try {\n await putConfigEntity(entityId, entityData);\n } catch (putConfigEntityError) {\n printMessage(putConfigEntityError, 'error');\n printMessage(`Error: ${putConfigEntityError}`, 'error');\n }\n}\n\n/**\n * Import all IDM configuration objects from separate JSON files in a directory specified by <directory>\n * @param baseDirectory export directory\n * @param validate validate script hooks\n */\nexport async function importAllRawConfigEntities(\n baseDirectory: string,\n validate?: boolean\n) {\n if (!fs.existsSync(baseDirectory)) {\n return;\n }\n const files = await readFiles(baseDirectory);\n const jsonFiles = files\n .filter(({ path }) => path.toLowerCase().endsWith('.json'))\n .map(({ path, content }) => ({\n // Remove .json extension\n entityId: path.substring(0, path.length - 5),\n content,\n path,\n }));\n\n let everyScriptValid = true;\n for (const file of jsonFiles) {\n const jsObject = JSON.parse(file.content);\n const isScriptValid = validateScriptHooks(jsObject);\n if (!isScriptValid) {\n printMessage(`Invalid script hook in ${file.path}`, 'error');\n everyScriptValid = false;\n }\n }\n\n if (validate && !everyScriptValid) {\n return;\n }\n\n createProgressIndicator(\n undefined,\n 'Importing config objects...',\n 'indeterminate'\n );\n\n const entityPromises = jsonFiles.map((file) => {\n return putConfigEntity(file.entityId, file.content);\n });\n\n await Promise.allSettled(entityPromises).then((results) => {\n const errors = results.filter(\n (result): result is PromiseRejectedResult => result.status === 'rejected'\n );\n\n if (errors.length > 0) {\n printMessage(\n `Failed to import ${errors.length} config objects:`,\n 'error'\n );\n errors.forEach((error) => {\n printMessage(`- ${error.reason}`, 'error');\n });\n stopProgressIndicator(\n `Failed to import ${errors.length} config objects`,\n 'error'\n );\n return;\n }\n\n stopProgressIndicator(\n `Imported ${results.length} config objects`,\n 'success'\n );\n });\n}\n\n/**\n * Import all IDM configuration objects\n * @param baseDirectory import directory\n * @param entitiesFile JSON file that specifies the config entities to export/import\n * @param envFile File that defines environment specific variables for replacement during configuration export/import\n * @param validate validate script hooks\n */\nexport async function importAllConfigEntities(\n baseDirectory: string,\n entitiesFile: string,\n envFile: string,\n validate?: boolean\n) {\n if (!fs.existsSync(baseDirectory)) {\n return;\n }\n const entriesToImport = JSON.parse(fs.readFileSync(entitiesFile, 'utf8')).idm;\n\n const envReader = propertiesReader(envFile);\n\n const files = await readFiles(baseDirectory);\n const jsonFiles = files\n .filter(({ path }) => path.toLowerCase().endsWith('.json'))\n .map(({ content, path }) => ({\n // Remove .json extension\n entityId: path.substring(0, path.length - 5),\n content,\n path,\n }));\n\n let everyScriptValid = true;\n for (const file of jsonFiles) {\n const jsObject = JSON.parse(file.content);\n const isScriptValid = validateScriptHooks(jsObject);\n if (!isScriptValid) {\n printMessage(`Invalid script hook in ${file.path}`, 'error');\n everyScriptValid = false;\n }\n }\n\n if (validate && !everyScriptValid) {\n return;\n }\n\n createProgressIndicator(\n undefined,\n 'Importing config objects...',\n 'indeterminate'\n );\n\n const entityPromises = jsonFiles\n .filter(({ entityId }) => {\n return entriesToImport.includes(entityId);\n })\n .map(({ entityId, content }) => {\n const unsubstituted = unSubstituteEnvParams(content, envReader);\n return putConfigEntity(entityId, unsubstituted);\n });\n\n await Promise.allSettled(entityPromises).then((results) => {\n const errors = results.filter(\n (result): result is PromiseRejectedResult => result.status === 'rejected'\n );\n\n if (errors.length > 0) {\n printMessage(\n `Failed to import ${errors.length} config objects:`,\n 'error'\n );\n errors.forEach((error) => {\n printMessage(`- ${error.reason}`, 'error');\n });\n stopProgressIndicator(\n `Failed to import ${errors.length} config objects`,\n 'error'\n );\n return;\n }\n\n stopProgressIndicator(\n `Imported ${results.length} config objects`,\n 'success'\n );\n });\n}\n\n/**\n * Count number of managed objects of a given type\n * @param {String} type managed object type, e.g. alpha_user\n */\nexport async function countManagedObjects(type) {\n let count = 0;\n let result = {\n result: [],\n resultCount: 0,\n pagedResultsCookie: null,\n totalPagedResultsPolicy: 'NONE',\n totalPagedResults: -1,\n remainingPagedResults: -1,\n };\n try {\n do {\n result = await queryAllManagedObjectsByType(\n type,\n [],\n result.pagedResultsCookie\n );\n count += result.resultCount;\n } while (result.pagedResultsCookie);\n printMessage(`${type}: ${count}`);\n } catch (error) {\n printMessage(error.response.data, 'error');\n printMessage(`Error querying managed objects by type: ${error}`, 'error');\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops/IdmOps.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAsB,oBAAoB,iBAEzC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,KAAA,gBAEjD;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,QAAQ,KAAA,gBAE7C;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,MAAM,gBAG5B;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,KAAA,EACJ,MAAM,KAAA,EACN,UAAU,KAAA,GACT,OAAO,CAAC;IACT,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC,CAED","file":"IdmOps.d.ts","sourcesContent":["import {\n getAllConfigEntities as getAllConfigEntitiesRaw,\n getConfigEntity as getConfigEntityRaw,\n putConfigEntity as putConfigEntityRaw,\n getConfigEntitiesByType as getConfigEntitiesByTypeRaw,\n queryAllManagedObjectsByType as queryAllManagedObjectsByTypeRaw,\n} from '../api/IdmConfigApi';\n\n/**\n * Get all IDM config entities\n * @returns {Promise} a promise that resolves to all IDM config entities\n */\nexport async function getAllConfigEntities() {\n return getAllConfigEntitiesRaw();\n}\n\n/**\n * Get IDM config entities by type\n * @param {String} type the desired type of config entity\n * @returns {Promise} a promise that resolves to an object containing all IDM config entities of the desired type\n */\nexport async function getConfigEntitiesByType(type) {\n return getConfigEntitiesByTypeRaw(type);\n}\n\n/**\n * Get an IDM config entity\n * @param {string} entityId the desired config entity\n * @returns {Promise<unknown>} a promise that resolves to an IDM config entity\n */\nexport async function getConfigEntity(entityId) {\n return getConfigEntityRaw(entityId);\n}\n\n/**\n * Put IDM config entity\n * @param {string} entityId config entity id\n * @param {string} entityData config entity object\n * @returns {Promise<unknown>} a promise that resolves to an IDM config entity\n */\nexport async function putConfigEntity(\n entityId: string,\n entityData: string | object\n) {\n return putConfigEntityRaw(entityId, entityData);\n}\n\n/**\n * Query managed objects\n * @param {String} type managed object type\n * @param {[String]} fields fields to retrieve\n * @param {String} pageCookie paged results cookie\n * @returns {Promise<{result: any[]; resultCount: number; pagedResultsCookie: any; totalPagedResultsPolicy: string; totalPagedResults: number; remainingPagedResults: number;}>} a promise that resolves to managed objects of the desired type\n */\nexport async function queryAllManagedObjectsByType(\n type,\n fields,\n pageCookie\n): Promise<{\n result: unknown[];\n resultCount: number;\n pagedResultsCookie: string;\n totalPagedResultsPolicy: string;\n totalPagedResults: number;\n remainingPagedResults: number;\n}> {\n return queryAllManagedObjectsByTypeRaw(type, fields, pageCookie);\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface JwkInterface {
|
|
2
|
+
kty: string;
|
|
3
|
+
use?: string;
|
|
4
|
+
key_ops?: string[];
|
|
5
|
+
alg: string;
|
|
6
|
+
kid?: string;
|
|
7
|
+
x5u?: string;
|
|
8
|
+
x5c?: string;
|
|
9
|
+
x5t?: string;
|
|
10
|
+
'x5t#S256'?: string;
|
|
11
|
+
}
|
|
12
|
+
export type JwkRsa = JwkInterface & {
|
|
13
|
+
d: string;
|
|
14
|
+
dp: string;
|
|
15
|
+
dq: string;
|
|
16
|
+
e: string;
|
|
17
|
+
n: string;
|
|
18
|
+
p: string;
|
|
19
|
+
q: string;
|
|
20
|
+
qi: string;
|
|
21
|
+
};
|
|
22
|
+
export type JwkRsaPublic = JwkInterface & {
|
|
23
|
+
e: string;
|
|
24
|
+
n: string;
|
|
25
|
+
};
|
|
26
|
+
export interface JwksInterface {
|
|
27
|
+
keys: JwkInterface[];
|
|
28
|
+
}
|
|
29
|
+
export declare function createJwkRsa(): Promise<JwkRsa>;
|
|
30
|
+
export declare function getJwkRsaPublic(jwkJson: JwkRsa): Promise<JwkRsaPublic>;
|
|
31
|
+
export declare function createJwks(...keys: JwkInterface[]): JwksInterface;
|
|
32
|
+
export declare function createSignedJwtToken(payload: any, jwkJson: JwkRsa): Promise<any>;
|
|
33
|
+
export declare function verifySignedJwtToken(jwt: string, jwkJson: JwkRsaPublic): Promise<any>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ops/JoseOps.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAIpD;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAI5E;AAED,wBAAgB,UAAU,CAAC,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,aAAa,CAIjE;AAED,wBAAsB,oBAAoB,CAAC,OAAO,KAAA,EAAE,OAAO,EAAE,MAAM,gBAalE;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,gBAI5E","file":"JoseOps.d.ts","sourcesContent":["import jose from 'node-jose';\n\nexport interface JwkInterface {\n kty: string;\n use?: string;\n key_ops?: string[];\n alg: string;\n kid?: string;\n x5u?: string;\n x5c?: string;\n x5t?: string;\n 'x5t#S256'?: string;\n}\n\nexport type JwkRsa = JwkInterface & {\n d: string;\n dp: string;\n dq: string;\n e: string;\n n: string;\n p: string;\n q: string;\n qi: string;\n};\n\nexport type JwkRsaPublic = JwkInterface & {\n e: string;\n n: string;\n};\n\nexport interface JwksInterface {\n keys: JwkInterface[];\n}\n\nexport async function createJwkRsa(): Promise<JwkRsa> {\n const jwk = await jose.JWK.createKey('RSA', 4096, { alg: 'RS256' });\n // include the private key\n return jwk.toJSON(true) as JwkRsa;\n}\n\nexport async function getJwkRsaPublic(jwkJson: JwkRsa): Promise<JwkRsaPublic> {\n const jwk = await jose.JWK.asKey(jwkJson);\n // do not include the private key\n return jwk.toJSON(false) as JwkRsaPublic;\n}\n\nexport function createJwks(...keys: JwkInterface[]): JwksInterface {\n return {\n keys,\n };\n}\n\nexport async function createSignedJwtToken(payload, jwkJson: JwkRsa) {\n const key = await jose.JWK.asKey(jwkJson);\n if (typeof payload === 'object') {\n payload = JSON.stringify(payload);\n }\n const jwt = await jose.JWS.createSign(\n { alg: 'RS256', compact: true, fields: {} },\n // https://github.com/cisco/node-jose/issues/253\n { key, reference: false }\n )\n .update(payload)\n .final();\n return jwt;\n}\n\nexport async function verifySignedJwtToken(jwt: string, jwkJson: JwkRsaPublic) {\n const jwk = await jose.JWK.asKey(jwkJson);\n const verifyResult = await jose.JWS.createVerify(jwk).verify(jwt);\n return verifyResult;\n}\n"]}
|