@rockcarver/frodo-lib 0.18.7 → 0.18.9-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.18.9-0] - 2023-03-23
11
+
12
+ ### Added
13
+
14
+ - rockcarver/frodo-cli#213: More debug logging for connection profile lookup by a unique substring. Use --debug to see the additional output. This is not yet a solution for rockcarver/frodo-cli#213 but should help identify the root cause.
15
+ - rockcarver/frodo-cli#216: More debug logging for the 2fa process and proper detection of unsupported webauthn factor.
16
+
17
+ ## [0.18.8] - 2023-02-17
18
+
19
+ ### Added
20
+
21
+ - Support for node 19.
22
+
10
23
  ## [0.18.7] - 2023-02-16
11
24
 
12
25
  ## [0.18.6] - 2023-02-16
@@ -1066,7 +1079,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1066
1079
  - Fixed problem with adding connection profiles
1067
1080
  - Miscellaneous bug fixes
1068
1081
 
1069
- [Unreleased]: https://github.com/rockcarver/frodo-lib/compare/v0.18.7...HEAD
1082
+ [Unreleased]: https://github.com/rockcarver/frodo-lib/compare/v0.18.9-0...HEAD
1083
+
1084
+ [0.18.9-0]: https://github.com/rockcarver/frodo-lib/compare/v0.18.8...v0.18.9-0
1085
+
1086
+ [0.18.8]: https://github.com/rockcarver/frodo-lib/compare/v0.18.7...v0.18.8
1070
1087
 
1071
1088
  [0.18.7]: https://github.com/rockcarver/frodo-lib/compare/v0.18.6...v0.18.7
1072
1089
 
@@ -20,6 +20,7 @@ var _uuid = require("uuid");
20
20
  var _ApiUtils = require("../api/utils/ApiUtils");
21
21
  var _JoseOps = require("./JoseOps");
22
22
  var _ServiceAccountOps = require("./cloud/ServiceAccountOps");
23
+ var _OpsUtils = require("./utils/OpsUtils");
23
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
24
25
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
25
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -59,6 +60,8 @@ function _determineCookieName() {
59
60
  return _determineCookieName.apply(this, arguments);
60
61
  }
61
62
  function checkAndHandle2FA(payload) {
63
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: start");
64
+ (0, _Console.debugMessage)(payload);
62
65
  // let skippable = false;
63
66
  if ('callbacks' in payload) {
64
67
  for (var element of payload.callbacks) {
@@ -66,8 +69,21 @@ function checkAndHandle2FA(payload) {
66
69
  if (element.input[0].value.includes('skip')) {
67
70
  // skippable = true;
68
71
  element.input[0].value = 'Skip';
72
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]");
69
73
  return {
70
74
  need2fa: true,
75
+ factor: 'None',
76
+ supported: true,
77
+ payload
78
+ };
79
+ }
80
+ if (element.input[0].value.includes('webAuthnOutcome')) {
81
+ // webauthn!!!
82
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]");
83
+ return {
84
+ need2fa: true,
85
+ factor: 'WebAuthN',
86
+ supported: false,
71
87
  payload
72
88
  };
73
89
  }
@@ -75,25 +91,33 @@ function checkAndHandle2FA(payload) {
75
91
  if (element.type === 'NameCallback') {
76
92
  if (element.output[0].value.includes('code')) {
77
93
  // skippable = false;
94
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false");
78
95
  (0, _Console.printMessage)('2FA is enabled and required for this user...');
79
96
  var code = _readlineSync.default.question("".concat(element.output[0].value, ": "));
80
97
  element.input[0].value = code;
98
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]");
81
99
  return {
82
100
  need2fa: true,
101
+ factor: 'Code',
102
+ supported: true,
83
103
  payload
84
104
  };
85
105
  }
86
106
  }
87
107
  }
88
- // console.info("NO2FA");
108
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: end [need2fa=false]");
89
109
  return {
90
110
  need2fa: false,
111
+ factor: 'None',
112
+ supported: true,
91
113
  payload
92
114
  };
93
115
  }
94
- // console.info("NO2FA");
116
+ (0, _Console.debugMessage)("AuthenticateOps.checkAndHandle2FA: end [need2fa=false]");
95
117
  return {
96
118
  need2fa: false,
119
+ factor: 'None',
120
+ supported: true,
97
121
  payload
98
122
  };
99
123
  }
@@ -206,6 +230,7 @@ function authenticate(_x2, _x3) {
206
230
  */
207
231
  function _authenticate() {
208
232
  _authenticate = _asyncToGenerator(function* (username, password) {
233
+ (0, _Console.debugMessage)("AuthenticateOps.authenticate: start");
209
234
  var config = {
210
235
  headers: {
211
236
  'X-OpenAM-Username': username,
@@ -214,6 +239,11 @@ function _authenticate() {
214
239
  };
215
240
  var response1 = yield (0, _AuthenticateApi.step)({}, config);
216
241
  var skip2FA = checkAndHandle2FA(response1);
242
+
243
+ // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN)
244
+ if (!skip2FA.supported) {
245
+ throw new Error("Unsupported 2FA factor: ".concat(skip2FA.factor));
246
+ }
217
247
  var response2 = {};
218
248
  if (skip2FA.need2fa) {
219
249
  response2 = yield (0, _AuthenticateApi.step)(skip2FA.payload);
@@ -221,8 +251,10 @@ function _authenticate() {
221
251
  response2 = skip2FA.payload;
222
252
  }
223
253
  if ('tokenId' in response2) {
254
+ (0, _Console.debugMessage)("AuthenticateOps.authenticate: end [tokenId=".concat(response2['tokenId'], "]"));
224
255
  return response2['tokenId'];
225
256
  }
257
+ (0, _Console.debugMessage)("AuthenticateOps.authenticate: end [no session]");
226
258
  return null;
227
259
  });
228
260
  return _authenticate.apply(this, arguments);
@@ -440,6 +472,17 @@ function _getTokens() {
440
472
  return false;
441
473
  }
442
474
  }
475
+
476
+ // if host is not a valid URL, try to locate a valid URL from connections.json
477
+ if (!(0, _OpsUtils.isValidUrl)(state.getHost())) {
478
+ var _conn = yield (0, _ConnectionProfileOps.getConnectionProfile)();
479
+ if (_conn) {
480
+ state.setHost(_conn.tenant);
481
+ } else {
482
+ return false;
483
+ }
484
+ }
485
+
443
486
  // now that we have the full tenant URL we can lookup the cookie name
444
487
  state.setCookieName(yield determineCookieName());
445
488
 
@@ -1 +1 @@
1
- {"version":3,"file":"AuthenticateOps.js","names":["adminClientPassword","redirectUrlTemplate","cloudIdmAdminScopes","forgeopsIdmAdminScopes","serviceAccountScopes","adminClientId","determineCookieName","data","getServerInfo","debugMessage","cookieName","error","printMessage","stack","checkAndHandle2FA","payload","element","callbacks","type","input","value","includes","need2fa","output","code","readlineSync","question","determineDefaultRealm","deploymentType","state","getRealm","globalConfig","DEFAULT_REALM_KEY","setRealm","DEPLOYMENT_TYPE_REALM_MAP","determineDeploymentType","cookieValue","getCookieValue","getUseBearerTokenForAmApis","CLOUD_DEPLOYMENT_TYPE_KEY","fidcClientId","forgeopsClientId","verifier","encodeBase64Url","randomBytes","challenge","createHash","update","digest","challengeMethod","redirectURL","url","resolve","getHost","config","maxRedirects","headers","getCookieName","bodyFormData","CLASSIC_DEPLOYMENT_TYPE_KEY","authorize","e","response","status","location","indexOf","verboseMessage","ex","FORGEOPS_DEPLOYMENT_TYPE_KEY","getSemanticVersion","versionInfo","versionString","version","rx","match","Error","authenticate","username","password","response1","step","skip2FA","response2","getAuthCode","codeChallenge","codeChallengeMethod","getDeploymentType","undefined","redirectLocationURL","queryObject","parse","query","message","getAccessTokenForUser","authCode","auth","accessToken","access_token","createPayload","serviceAccountId","u","parseUrl","aud","origin","port","protocol","pathname","exp","Math","floor","Date","getTime","jti","v4","iss","sub","getAccessTokenForServiceAccount","jwk","jwt","createSignedJwtToken","determineDeploymentTypeAndDefaultRealmAndVersion","setDeploymentType","getServerVersionInfo","fullVersion","setAmVersion","getLoggedInSubject","subjectString","getUsername","name","getServiceAccount","getServiceAccountId","getTokens","forceLoginAsUser","getPassword","getServiceAccountJwk","conn","getConnectionProfile","setHost","tenant","setUsername","setPassword","setAuthenticationService","authenticationService","setAuthenticationHeaderOverrides","authenticationHeaderOverrides","setServiceAccountId","svcacctId","setServiceAccountJwk","svcacctJwk","setCookieName","token","setBearerToken","setUseBearerTokenForAmApis","saErr","error_description","setCookieValue","getBearerToken"],"sources":["ops/AuthenticateOps.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 { getServiceAccount } from './cloud/ServiceAccountOps';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst cloudIdmAdminScopes = 'openid fr:idm:* fr:idc:esv:*';\nconst forgeopsIdmAdminScopes = 'openid fr:idm:*';\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=${cloudIdmAdminScopes}&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=${forgeopsIdmAdminScopes}&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=${\n state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY\n ? cloudIdmAdminScopes\n : forgeopsIdmAdminScopes\n }&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 debugMessage(`AuthenticateOps.getAccessTokenForUser: start`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end with token`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end without token`);\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();\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 = (await getServiceAccount(state.getServiceAccountId())).name;\n subjectString = `service account ${name} [${state.getServiceAccountId()}]`;\n }\n return subjectString;\n}\n\n/**\n * Get tokens\n * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false)\n * @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(forceLoginAsUser = false): Promise<boolean> {\n debugMessage(`AuthenticateOps.getTokens: start`);\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 (\n !forceLoginAsUser &&\n state.getServiceAccountId() &&\n state.getServiceAccountJwk()\n ) {\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 debugMessage(saErr.response?.data || saErr);\n throw new Error(\n `Service account login error: ${\n saErr.response?.data?.error_description ||\n saErr.response?.data?.message ||\n saErr\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 debugMessage(`AuthenticateOps.getTokens: end with tokens`);\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 debugMessage(`AuthenticateOps.getTokens: end without tokens`);\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AuthenticateOps.js","names":["adminClientPassword","redirectUrlTemplate","cloudIdmAdminScopes","forgeopsIdmAdminScopes","serviceAccountScopes","adminClientId","determineCookieName","data","getServerInfo","debugMessage","cookieName","error","printMessage","stack","checkAndHandle2FA","payload","element","callbacks","type","input","value","includes","need2fa","factor","supported","output","code","readlineSync","question","determineDefaultRealm","deploymentType","state","getRealm","globalConfig","DEFAULT_REALM_KEY","setRealm","DEPLOYMENT_TYPE_REALM_MAP","determineDeploymentType","cookieValue","getCookieValue","getUseBearerTokenForAmApis","CLOUD_DEPLOYMENT_TYPE_KEY","fidcClientId","forgeopsClientId","verifier","encodeBase64Url","randomBytes","challenge","createHash","update","digest","challengeMethod","redirectURL","url","resolve","getHost","config","maxRedirects","headers","getCookieName","bodyFormData","CLASSIC_DEPLOYMENT_TYPE_KEY","authorize","e","response","status","location","indexOf","verboseMessage","ex","FORGEOPS_DEPLOYMENT_TYPE_KEY","getSemanticVersion","versionInfo","versionString","version","rx","match","Error","authenticate","username","password","response1","step","skip2FA","response2","getAuthCode","codeChallenge","codeChallengeMethod","getDeploymentType","undefined","redirectLocationURL","queryObject","parse","query","message","getAccessTokenForUser","authCode","auth","accessToken","access_token","createPayload","serviceAccountId","u","parseUrl","aud","origin","port","protocol","pathname","exp","Math","floor","Date","getTime","jti","v4","iss","sub","getAccessTokenForServiceAccount","jwk","jwt","createSignedJwtToken","determineDeploymentTypeAndDefaultRealmAndVersion","setDeploymentType","getServerVersionInfo","fullVersion","setAmVersion","getLoggedInSubject","subjectString","getUsername","name","getServiceAccount","getServiceAccountId","getTokens","forceLoginAsUser","getPassword","getServiceAccountJwk","conn","getConnectionProfile","setHost","tenant","setUsername","setPassword","setAuthenticationService","authenticationService","setAuthenticationHeaderOverrides","authenticationHeaderOverrides","setServiceAccountId","svcacctId","setServiceAccountJwk","svcacctJwk","isValidUrl","setCookieName","token","setBearerToken","setUseBearerTokenForAmApis","saErr","error_description","setCookieValue","getBearerToken"],"sources":["ops/AuthenticateOps.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 { getServiceAccount } from './cloud/ServiceAccountOps';\nimport { isValidUrl } from './utils/OpsUtils';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst cloudIdmAdminScopes = 'openid fr:idm:* fr:idc:esv:*';\nconst forgeopsIdmAdminScopes = 'openid fr:idm:*';\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 debugMessage(`AuthenticateOps.checkAndHandle2FA: start`);\n debugMessage(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 debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]`\n );\n return {\n need2fa: true,\n factor: 'None',\n supported: true,\n payload,\n };\n }\n if (element.input[0].value.includes('webAuthnOutcome')) {\n // webauthn!!!\n debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]`\n );\n return {\n need2fa: true,\n factor: 'WebAuthN',\n supported: false,\n payload,\n };\n }\n }\n if (element.type === 'NameCallback') {\n if (element.output[0].value.includes('code')) {\n // skippable = false;\n debugMessage(\n `AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false`\n );\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 debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]`\n );\n return {\n need2fa: true,\n factor: 'Code',\n supported: true,\n payload,\n };\n }\n }\n }\n debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);\n return {\n need2fa: false,\n factor: 'None',\n supported: true,\n payload,\n };\n }\n debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);\n return {\n need2fa: false,\n factor: 'None',\n supported: true,\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=${cloudIdmAdminScopes}&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=${forgeopsIdmAdminScopes}&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 debugMessage(`AuthenticateOps.authenticate: start`);\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\n // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN)\n if (!skip2FA.supported) {\n throw new Error(`Unsupported 2FA factor: ${skip2FA.factor}`);\n }\n\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 debugMessage(\n `AuthenticateOps.authenticate: end [tokenId=${response2['tokenId']}]`\n );\n return response2['tokenId'] as string;\n }\n debugMessage(`AuthenticateOps.authenticate: end [no session]`);\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=${\n state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY\n ? cloudIdmAdminScopes\n : forgeopsIdmAdminScopes\n }&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 debugMessage(`AuthenticateOps.getAccessTokenForUser: start`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end with token`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end without token`);\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();\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 = (await getServiceAccount(state.getServiceAccountId())).name;\n subjectString = `service account ${name} [${state.getServiceAccountId()}]`;\n }\n return subjectString;\n}\n\n/**\n * Get tokens\n * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false)\n * @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(forceLoginAsUser = false): Promise<boolean> {\n debugMessage(`AuthenticateOps.getTokens: start`);\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\n // if host is not a valid URL, try to locate a valid URL from connections.json\n if (!isValidUrl(state.getHost())) {\n const conn = await getConnectionProfile();\n if (conn) {\n state.setHost(conn.tenant);\n } else {\n return false;\n }\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 (\n !forceLoginAsUser &&\n state.getServiceAccountId() &&\n state.getServiceAccountJwk()\n ) {\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 debugMessage(saErr.response?.data || saErr);\n throw new Error(\n `Service account login error: ${\n saErr.response?.data?.error_description ||\n saErr.response?.data?.message ||\n saErr\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 debugMessage(`AuthenticateOps.getTokens: end with tokens`);\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 debugMessage(`AuthenticateOps.getTokens: end without tokens`);\n return false;\n}\n"],"mappings":"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"}
@@ -57,7 +57,9 @@ function getConnectionProfilesPath() {
57
57
  function findConnectionProfiles(connectionProfiles, host) {
58
58
  var profiles = [];
59
59
  for (var tenant in connectionProfiles) {
60
+ (0, _Console.debugMessage)("ConnectionProfileOps.findConnectionProfiles: tenant=".concat(tenant));
60
61
  if (tenant.includes(host)) {
62
+ (0, _Console.debugMessage)("ConnectionProfileOps.findConnectionProfiles: '".concat(host, "' identifies '").concat(tenant, "', including in result set"));
61
63
  var foundProfile = _objectSpread({}, connectionProfiles[tenant]);
62
64
  foundProfile.tenant = tenant;
63
65
  profiles.push(foundProfile);
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectionProfileOps.js","names":["crypto","DataProtection","fileOptions","indentation","legacyProfileFilename","newProfileFilename","getConnectionProfilesPath","state","process","env","FRODO_CONNECTION_PROFILES_PATH_KEY","os","homedir","findConnectionProfiles","connectionProfiles","host","profiles","tenant","includes","foundProfile","push","listConnectionProfiles","long","filename","data","fs","readFileSync","connectionsData","JSON","parse","Object","keys","length","printMessage","table","createTable","forEach","c","svcacctName","svcacctId","username","logApiKey","toString","e","message","migrateFromLegacyProfile","legacyPath","newPath","existsSync","writeFileSync","stringify","copyFileSync","renameSync","initConnectionProfiles","folderName","path","dirname","mkdirSync","recursive","convert","conn","encodedPassword","encrypt","encodedLogApiSecret","encodedSvcacctJwk","getConnectionProfileByHost","p","password","decrypt","logApiSecret","authenticationService","authenticationHeaderOverrides","svcacctJwk","getConnectionProfile","getHost","saveConnectionProfile","debugMessage","profile","statSync","found","setHost","verboseMessage","isValidUrl","error","getUsername","getPassword","getLogApiKey","getLogApiSecret","getServiceAccountId","getServiceAccount","name","getServiceAccountJwk","getAuthenticationService","getAuthenticationHeaderOverrides","entries","orderedProfiles","sort","reduce","obj","key","saveJsonToFile","deleteConnectionProfile","stat","err","code","describeConnectionProfile","showSecrets","present","jwk","keyMap","createObjectTable","addNewServiceAccount","Date","getTime","description","scope","jwkPrivate","createJwkRsa","jwkPublic","getJwkRsaPublic","jwks","createJwks","sa","createServiceAccount","_id","setServiceAccountId","setServiceAccountJwk"],"sources":["ops/ConnectionProfileOps.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 {\n createServiceAccount,\n getServiceAccount,\n} from './cloud/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 (Object.keys(connectionsData).length < 1) {\n printMessage(`No connections defined yet in ${filename}`, 'info');\n } else {\n if (long) {\n const table = createTable([\n 'Host',\n 'Service Account',\n 'Username',\n 'Log API Key',\n ]);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].svcacctName || connectionsData[c].svcacctId,\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 // getUniqueNames(5, Object.keys(connectionsData));\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 }\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 const newPath = `${os.homedir()}/.frodo/${newProfileFilename}`;\n if (!fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // no connections file (old or new), create empty new one\n fs.writeFileSync(\n newPath,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n } else if (fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // old exists, new one does not - so copy old to new one\n fs.copyFileSync(legacyPath, newPath);\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 // in other cases, where\n // (both old and new exist) OR (only new one exists) don't do anything\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 svcacctName: profiles[0].svcacctName ? profiles[0].svcacctName : null,\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: start`);\n const filename = getConnectionProfilesPath();\n debugMessage(`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 debugMessage(profile);\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 debugMessage(`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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [false]`);\n return false;\n }\n }\n } catch (error) {\n debugMessage(`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 profile.svcacctName = (\n await getServiceAccount(state.getServiceAccountId())\n ).name;\n }\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n // update existing service account profile\n if (profile.svcacctId && !profile.svcacctName) {\n profile.svcacctName = (await getServiceAccount(profile.svcacctId)).name;\n debugMessage(\n `ConnectionProfileOps.saveConnectionProfile: added missing service account name`\n );\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [true]`);\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: start`);\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n debugMessage(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 delete profile.svcacctName;\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 svcacctName: 'Service Account Name',\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: end`);\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"],"mappings":"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"}
1
+ {"version":3,"file":"ConnectionProfileOps.js","names":["crypto","DataProtection","fileOptions","indentation","legacyProfileFilename","newProfileFilename","getConnectionProfilesPath","state","process","env","FRODO_CONNECTION_PROFILES_PATH_KEY","os","homedir","findConnectionProfiles","connectionProfiles","host","profiles","tenant","debugMessage","includes","foundProfile","push","listConnectionProfiles","long","filename","data","fs","readFileSync","connectionsData","JSON","parse","Object","keys","length","printMessage","table","createTable","forEach","c","svcacctName","svcacctId","username","logApiKey","toString","e","message","migrateFromLegacyProfile","legacyPath","newPath","existsSync","writeFileSync","stringify","copyFileSync","renameSync","initConnectionProfiles","folderName","path","dirname","mkdirSync","recursive","convert","conn","encodedPassword","encrypt","encodedLogApiSecret","encodedSvcacctJwk","getConnectionProfileByHost","p","password","decrypt","logApiSecret","authenticationService","authenticationHeaderOverrides","svcacctJwk","getConnectionProfile","getHost","saveConnectionProfile","profile","statSync","found","setHost","verboseMessage","isValidUrl","error","getUsername","getPassword","getLogApiKey","getLogApiSecret","getServiceAccountId","getServiceAccount","name","getServiceAccountJwk","getAuthenticationService","getAuthenticationHeaderOverrides","entries","orderedProfiles","sort","reduce","obj","key","saveJsonToFile","deleteConnectionProfile","stat","err","code","describeConnectionProfile","showSecrets","present","jwk","keyMap","createObjectTable","addNewServiceAccount","Date","getTime","description","scope","jwkPrivate","createJwkRsa","jwkPublic","getJwkRsaPublic","jwks","createJwks","sa","createServiceAccount","_id","setServiceAccountId","setServiceAccountJwk"],"sources":["ops/ConnectionProfileOps.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 {\n createServiceAccount,\n getServiceAccount,\n} from './cloud/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 debugMessage(\n `ConnectionProfileOps.findConnectionProfiles: tenant=${tenant}`\n );\n if (tenant.includes(host)) {\n debugMessage(\n `ConnectionProfileOps.findConnectionProfiles: '${host}' identifies '${tenant}', including in result set`\n );\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 (Object.keys(connectionsData).length < 1) {\n printMessage(`No connections defined yet in ${filename}`, 'info');\n } else {\n if (long) {\n const table = createTable([\n 'Host',\n 'Service Account',\n 'Username',\n 'Log API Key',\n ]);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].svcacctName || connectionsData[c].svcacctId,\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 // getUniqueNames(5, Object.keys(connectionsData));\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 }\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 const newPath = `${os.homedir()}/.frodo/${newProfileFilename}`;\n if (!fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // no connections file (old or new), create empty new one\n fs.writeFileSync(\n newPath,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n } else if (fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // old exists, new one does not - so copy old to new one\n fs.copyFileSync(legacyPath, newPath);\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 // in other cases, where\n // (both old and new exist) OR (only new one exists) don't do anything\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 svcacctName: profiles[0].svcacctName ? profiles[0].svcacctName : null,\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: start`);\n const filename = getConnectionProfilesPath();\n debugMessage(`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 debugMessage(profile);\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 debugMessage(`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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [false]`);\n return false;\n }\n }\n } catch (error) {\n debugMessage(`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 profile.svcacctName = (\n await getServiceAccount(state.getServiceAccountId())\n ).name;\n }\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n // update existing service account profile\n if (profile.svcacctId && !profile.svcacctName) {\n profile.svcacctName = (await getServiceAccount(profile.svcacctId)).name;\n debugMessage(\n `ConnectionProfileOps.saveConnectionProfile: added missing service account name`\n );\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [true]`);\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: start`);\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n debugMessage(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 delete profile.svcacctName;\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 svcacctName: 'Service Account Name',\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: end`);\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"],"mappings":"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"}
@@ -119,7 +119,8 @@ function _scheduleShutdown() {
119
119
  }
120
120
  function getFrodoCommand() {
121
121
  try {
122
- if (!process.argv[1].endsWith('frodo') && !process.argv[1].endsWith('frodo.exe')) {
122
+ if (mode !== _utils.MODES.RECORD) (0, _Console.debugMessage)("SetupPollyForFrodoLib.getFrodoCommand: process.argv=".concat(process.argv));
123
+ if (!process.argv[1].endsWith('frodo') && !process.argv[1].endsWith('frodo.exe') && !process.argv[1].endsWith('app.js')) {
123
124
  return _path.default.parse(process.argv[1]).name.replace('-', '/');
124
125
  }
125
126
  return process.argv[2];
@@ -1 +1 @@
1
- {"version":3,"file":"SetupPollyForFrodoLib.js","names":["__dirname","path","dirname","fileURLToPath","FRODO_MOCK_HOSTS","recordIfMissing","mode","MODES","REPLAY","recordingsDir","replace","process","env","FRODO_MOCK","Polly","register","NodeHttpAdapter","FSPersister","RECORD","defaultMatchRequestsBy","JSON","parse","stringify","method","headers","body","order","url","protocol","username","password","hostname","port","pathname","query","hash","authenticationMatchRequestsBy","matchRequestsBy","delay","ms","Promise","resolve","setTimeout","countdown","ttl","timeout","scheduleShutdown","polly","console","log","getFrodoCommand","stop","argv","endsWith","name","error","printMessage","setupPollyForFrodoLib","configure","adapters","flushRequestsOnStop","logLevel","FRODO_POLLY_LOG_LEVEL","recordFailedRequests","persister","persisterOptions","fs","host","server","any","recordingName","on","req","debugMessage","config"],"sources":["utils/SetupPollyForFrodoLib.ts"],"sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Polly } from '@pollyjs/core';\nimport { MODES } from '@pollyjs/utils';\nimport NodeHttpAdapter from '@pollyjs/adapter-node-http';\nimport FSPersister from '@pollyjs/persister-fs';\nimport { LogLevelDesc } from 'loglevel';\nimport { debugMessage, printMessage } from '../ops/utils/Console';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst FRODO_MOCK_HOSTS = [\n 'https://openam-frodo-dev.forgeblocks.com',\n 'https://openam-service-accounts.forgeblocks.com',\n 'https://openam-volker-dev.forgeblocks.com',\n];\n\nlet recordIfMissing = false;\nlet mode = MODES.REPLAY;\n\n// resolve \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/esm/api\" to\n// \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/src/test/recordings\"\nconst recordingsDir = __dirname.replace(\n /^(.*\\/frodo-\\w{3})(.*)$/gi,\n '$1/mocks'\n);\n\nif (process.env.FRODO_MOCK) {\n Polly.register(NodeHttpAdapter);\n Polly.register(FSPersister);\n if (process.env.FRODO_MOCK === 'record') {\n mode = MODES.RECORD;\n recordIfMissing = true;\n }\n}\n\nfunction defaultMatchRequestsBy() {\n return JSON.parse(\n JSON.stringify({\n method: true,\n headers: false, // do not match headers, because \"Authorization\" header is sent only at recording time\n body: true,\n order: false,\n url: {\n protocol: false,\n username: false,\n password: false,\n hostname: false, // we will record from different envs but run tests always against `frodo-dev`\n port: false,\n pathname: true,\n query: true,\n hash: true,\n },\n })\n );\n}\n\nfunction authenticationMatchRequestsBy() {\n const matchRequestsBy = defaultMatchRequestsBy();\n matchRequestsBy.body = false;\n matchRequestsBy.order = true;\n return matchRequestsBy;\n}\n\n// returns a delayed promise\nasync function delay(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function countdown(ms) {\n await delay(ms);\n return --ttl;\n}\n\nconst timeout = 15;\nlet ttl = timeout;\nasync function scheduleShutdown(polly: Polly) {\n ++ttl;\n while (await countdown(1000)) {\n if (ttl < 4)\n console.log(\n `Polly instance '${getFrodoCommand()}' stopping in ${ttl}s...`\n );\n }\n await polly.stop();\n console.log(`Polly instance '${getFrodoCommand()}' stopped.`);\n}\n\n/*\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/usr/local/bin/frodo',\n 'journey',\n 'list',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/Users/vscheuber/Projects/frodo-cli/esm/cli/journey/journey-list.js',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\n*/\nfunction getFrodoCommand() {\n try {\n if (\n !process.argv[1].endsWith('frodo') &&\n !process.argv[1].endsWith('frodo.exe')\n ) {\n return path.parse(process.argv[1]).name.replace('-', '/');\n }\n return process.argv[2];\n } catch (error) {\n printMessage(`SetupPollyForFrodoLib.getFrodoCommand: ${error}`, 'error');\n printMessage(process.argv, 'error');\n return 'error';\n }\n}\n\nexport function setupPollyForFrodoLib(\n matchRequestsBy = defaultMatchRequestsBy()\n): Polly {\n const polly = new Polly('default');\n\n polly.configure({\n adapters: ['node-http'],\n mode,\n recordIfMissing,\n flushRequestsOnStop: true,\n logLevel: (process.env.FRODO_POLLY_LOG_LEVEL as LogLevelDesc) || 'warn',\n recordFailedRequests: true,\n persister: 'fs',\n persisterOptions: {\n fs: {\n recordingsDir,\n },\n },\n matchRequestsBy,\n });\n\n for (const host of FRODO_MOCK_HOSTS) {\n if (mode === MODES.RECORD) console.log(`***** Host: ${host}`);\n polly.server.host(host, () => {\n polly.server\n .any('/am/oauth2/*')\n .recordingName(`${getFrodoCommand()}/oauth2`)\n .on('request', (req) => {\n req.configure({ matchRequestsBy: authenticationMatchRequestsBy() });\n });\n polly.server.any('/am/json/*').recordingName(`${getFrodoCommand()}/am`);\n polly.server\n .any('/openidm/*')\n .recordingName(`${getFrodoCommand()}/openidm`);\n polly.server\n .any('/environment/*')\n .recordingName(`${getFrodoCommand()}/environment`);\n polly.server\n .any('/monitoring/*')\n .recordingName(`${getFrodoCommand()}/monitoring`);\n polly.server\n .any('/feature')\n .recordingName(`${getFrodoCommand()}/feature`);\n polly.server\n .any('/dashboard/*')\n .recordingName(`${getFrodoCommand()}/dashboard`);\n });\n }\n polly.server.host('https://api.github.com', () => {\n polly.server.any('/*').recordingName(`github`);\n });\n polly.server.host('https://registry.npmjs.org', () => {\n polly.server.any('/*').recordingName(`npmjs`);\n });\n polly.server.any().on('request', () => {\n if (ttl < timeout) {\n // console.log(`Reset polly stop ttl (${ttl}) to ${timeout}`);\n ttl = timeout;\n }\n });\n\n if (mode === MODES.RECORD) {\n scheduleShutdown(polly);\n } else {\n // only output debug messages if not recording as this polly instance is\n // primarily used by frodo-cli e2e tests, which capture stdout in snapshots.\n // debug messages falsify the snapshot recordings.\n debugMessage(`Polly config:`);\n debugMessage(polly.config);\n }\n\n return polly;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SetupPollyForFrodoLib.js","names":["__dirname","path","dirname","fileURLToPath","FRODO_MOCK_HOSTS","recordIfMissing","mode","MODES","REPLAY","recordingsDir","replace","process","env","FRODO_MOCK","Polly","register","NodeHttpAdapter","FSPersister","RECORD","defaultMatchRequestsBy","JSON","parse","stringify","method","headers","body","order","url","protocol","username","password","hostname","port","pathname","query","hash","authenticationMatchRequestsBy","matchRequestsBy","delay","ms","Promise","resolve","setTimeout","countdown","ttl","timeout","scheduleShutdown","polly","console","log","getFrodoCommand","stop","debugMessage","argv","endsWith","name","error","printMessage","setupPollyForFrodoLib","configure","adapters","flushRequestsOnStop","logLevel","FRODO_POLLY_LOG_LEVEL","recordFailedRequests","persister","persisterOptions","fs","host","server","any","recordingName","on","req","config"],"sources":["utils/SetupPollyForFrodoLib.ts"],"sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Polly } from '@pollyjs/core';\nimport { MODES } from '@pollyjs/utils';\nimport NodeHttpAdapter from '@pollyjs/adapter-node-http';\nimport FSPersister from '@pollyjs/persister-fs';\nimport { LogLevelDesc } from 'loglevel';\nimport { debugMessage, printMessage } from '../ops/utils/Console';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst FRODO_MOCK_HOSTS = [\n 'https://openam-frodo-dev.forgeblocks.com',\n 'https://openam-service-accounts.forgeblocks.com',\n 'https://openam-volker-dev.forgeblocks.com',\n];\n\nlet recordIfMissing = false;\nlet mode = MODES.REPLAY;\n\n// resolve \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/esm/api\" to\n// \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/src/test/recordings\"\nconst recordingsDir = __dirname.replace(\n /^(.*\\/frodo-\\w{3})(.*)$/gi,\n '$1/mocks'\n);\n\nif (process.env.FRODO_MOCK) {\n Polly.register(NodeHttpAdapter);\n Polly.register(FSPersister);\n if (process.env.FRODO_MOCK === 'record') {\n mode = MODES.RECORD;\n recordIfMissing = true;\n }\n}\n\nfunction defaultMatchRequestsBy() {\n return JSON.parse(\n JSON.stringify({\n method: true,\n headers: false, // do not match headers, because \"Authorization\" header is sent only at recording time\n body: true,\n order: false,\n url: {\n protocol: false,\n username: false,\n password: false,\n hostname: false, // we will record from different envs but run tests always against `frodo-dev`\n port: false,\n pathname: true,\n query: true,\n hash: true,\n },\n })\n );\n}\n\nfunction authenticationMatchRequestsBy() {\n const matchRequestsBy = defaultMatchRequestsBy();\n matchRequestsBy.body = false;\n matchRequestsBy.order = true;\n return matchRequestsBy;\n}\n\n// returns a delayed promise\nasync function delay(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function countdown(ms) {\n await delay(ms);\n return --ttl;\n}\n\nconst timeout = 15;\nlet ttl = timeout;\nasync function scheduleShutdown(polly: Polly) {\n ++ttl;\n while (await countdown(1000)) {\n if (ttl < 4)\n console.log(\n `Polly instance '${getFrodoCommand()}' stopping in ${ttl}s...`\n );\n }\n await polly.stop();\n console.log(`Polly instance '${getFrodoCommand()}' stopped.`);\n}\n\n/*\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/usr/local/bin/frodo',\n 'journey',\n 'list',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/Users/vscheuber/Projects/frodo-cli/esm/cli/journey/journey-list.js',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\n*/\nfunction getFrodoCommand() {\n try {\n if (mode !== MODES.RECORD)\n debugMessage(\n `SetupPollyForFrodoLib.getFrodoCommand: process.argv=${process.argv}`\n );\n if (\n !process.argv[1].endsWith('frodo') &&\n !process.argv[1].endsWith('frodo.exe') &&\n !process.argv[1].endsWith('app.js')\n ) {\n return path.parse(process.argv[1]).name.replace('-', '/');\n }\n return process.argv[2];\n } catch (error) {\n printMessage(`SetupPollyForFrodoLib.getFrodoCommand: ${error}`, 'error');\n printMessage(process.argv, 'error');\n return 'error';\n }\n}\n\nexport function setupPollyForFrodoLib(\n matchRequestsBy = defaultMatchRequestsBy()\n): Polly {\n const polly = new Polly('default');\n\n polly.configure({\n adapters: ['node-http'],\n mode,\n recordIfMissing,\n flushRequestsOnStop: true,\n logLevel: (process.env.FRODO_POLLY_LOG_LEVEL as LogLevelDesc) || 'warn',\n recordFailedRequests: true,\n persister: 'fs',\n persisterOptions: {\n fs: {\n recordingsDir,\n },\n },\n matchRequestsBy,\n });\n\n for (const host of FRODO_MOCK_HOSTS) {\n if (mode === MODES.RECORD) console.log(`***** Host: ${host}`);\n polly.server.host(host, () => {\n polly.server\n .any('/am/oauth2/*')\n .recordingName(`${getFrodoCommand()}/oauth2`)\n .on('request', (req) => {\n req.configure({ matchRequestsBy: authenticationMatchRequestsBy() });\n });\n polly.server.any('/am/json/*').recordingName(`${getFrodoCommand()}/am`);\n polly.server\n .any('/openidm/*')\n .recordingName(`${getFrodoCommand()}/openidm`);\n polly.server\n .any('/environment/*')\n .recordingName(`${getFrodoCommand()}/environment`);\n polly.server\n .any('/monitoring/*')\n .recordingName(`${getFrodoCommand()}/monitoring`);\n polly.server\n .any('/feature')\n .recordingName(`${getFrodoCommand()}/feature`);\n polly.server\n .any('/dashboard/*')\n .recordingName(`${getFrodoCommand()}/dashboard`);\n });\n }\n polly.server.host('https://api.github.com', () => {\n polly.server.any('/*').recordingName(`github`);\n });\n polly.server.host('https://registry.npmjs.org', () => {\n polly.server.any('/*').recordingName(`npmjs`);\n });\n polly.server.any().on('request', () => {\n if (ttl < timeout) {\n // console.log(`Reset polly stop ttl (${ttl}) to ${timeout}`);\n ttl = timeout;\n }\n });\n\n if (mode === MODES.RECORD) {\n scheduleShutdown(polly);\n } else {\n // only output debug messages if not recording as this polly instance is\n // primarily used by frodo-cli e2e tests, which capture stdout in snapshots.\n // debug messages falsify the snapshot recordings.\n debugMessage(`Polly config:`);\n debugMessage(polly.config);\n }\n\n return polly;\n}\n"],"mappings":"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"}
@@ -13,6 +13,7 @@ import { v4 } from 'uuid';
13
13
  import { parseUrl } from '../api/utils/ApiUtils';
14
14
  import { createSignedJwtToken } from './JoseOps';
15
15
  import { getServiceAccount } from './cloud/ServiceAccountOps';
16
+ import { isValidUrl } from './utils/OpsUtils';
16
17
  const adminClientPassword = 'doesnotmatter';
17
18
  const redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';
18
19
  const cloudIdmAdminScopes = 'openid fr:idm:* fr:idc:esv:*';
@@ -42,6 +43,8 @@ async function determineCookieName() {
42
43
  * @returns {Object} an object indicating if 2fa is required and the original payload
43
44
  */
44
45
  function checkAndHandle2FA(payload) {
46
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: start`);
47
+ debugMessage(payload);
45
48
  // let skippable = false;
46
49
  if ('callbacks' in payload) {
47
50
  for (const element of payload.callbacks) {
@@ -49,8 +52,21 @@ function checkAndHandle2FA(payload) {
49
52
  if (element.input[0].value.includes('skip')) {
50
53
  // skippable = true;
51
54
  element.input[0].value = 'Skip';
55
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]`);
52
56
  return {
53
57
  need2fa: true,
58
+ factor: 'None',
59
+ supported: true,
60
+ payload
61
+ };
62
+ }
63
+ if (element.input[0].value.includes('webAuthnOutcome')) {
64
+ // webauthn!!!
65
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]`);
66
+ return {
67
+ need2fa: true,
68
+ factor: 'WebAuthN',
69
+ supported: false,
54
70
  payload
55
71
  };
56
72
  }
@@ -58,25 +74,33 @@ function checkAndHandle2FA(payload) {
58
74
  if (element.type === 'NameCallback') {
59
75
  if (element.output[0].value.includes('code')) {
60
76
  // skippable = false;
77
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false`);
61
78
  printMessage('2FA is enabled and required for this user...');
62
79
  const code = readlineSync.question(`${element.output[0].value}: `);
63
80
  element.input[0].value = code;
81
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]`);
64
82
  return {
65
83
  need2fa: true,
84
+ factor: 'Code',
85
+ supported: true,
66
86
  payload
67
87
  };
68
88
  }
69
89
  }
70
90
  }
71
- // console.info("NO2FA");
91
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);
72
92
  return {
73
93
  need2fa: false,
94
+ factor: 'None',
95
+ supported: true,
74
96
  payload
75
97
  };
76
98
  }
77
- // console.info("NO2FA");
99
+ debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);
78
100
  return {
79
101
  need2fa: false,
102
+ factor: 'None',
103
+ supported: true,
80
104
  payload
81
105
  };
82
106
  }
@@ -168,6 +192,7 @@ async function getSemanticVersion(versionInfo) {
168
192
  * @returns {string} Session token or null
169
193
  */
170
194
  async function authenticate(username, password) {
195
+ debugMessage(`AuthenticateOps.authenticate: start`);
171
196
  const config = {
172
197
  headers: {
173
198
  'X-OpenAM-Username': username,
@@ -176,6 +201,11 @@ async function authenticate(username, password) {
176
201
  };
177
202
  const response1 = await step({}, config);
178
203
  const skip2FA = checkAndHandle2FA(response1);
204
+
205
+ // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN)
206
+ if (!skip2FA.supported) {
207
+ throw new Error(`Unsupported 2FA factor: ${skip2FA.factor}`);
208
+ }
179
209
  let response2 = {};
180
210
  if (skip2FA.need2fa) {
181
211
  response2 = await step(skip2FA.payload);
@@ -183,8 +213,10 @@ async function authenticate(username, password) {
183
213
  response2 = skip2FA.payload;
184
214
  }
185
215
  if ('tokenId' in response2) {
216
+ debugMessage(`AuthenticateOps.authenticate: end [tokenId=${response2['tokenId']}]`);
186
217
  return response2['tokenId'];
187
218
  }
219
+ debugMessage(`AuthenticateOps.authenticate: end [no session]`);
188
220
  return null;
189
221
  }
190
222
 
@@ -375,6 +407,17 @@ export async function getTokens(forceLoginAsUser = false) {
375
407
  return false;
376
408
  }
377
409
  }
410
+
411
+ // if host is not a valid URL, try to locate a valid URL from connections.json
412
+ if (!isValidUrl(state.getHost())) {
413
+ const conn = await getConnectionProfile();
414
+ if (conn) {
415
+ state.setHost(conn.tenant);
416
+ } else {
417
+ return false;
418
+ }
419
+ }
420
+
378
421
  // now that we have the full tenant URL we can lookup the cookie name
379
422
  state.setCookieName(await determineCookieName());
380
423
 
@@ -33,7 +33,9 @@ export function getConnectionProfilesPath() {
33
33
  function findConnectionProfiles(connectionProfiles, host) {
34
34
  const profiles = [];
35
35
  for (const tenant in connectionProfiles) {
36
+ debugMessage(`ConnectionProfileOps.findConnectionProfiles: tenant=${tenant}`);
36
37
  if (tenant.includes(host)) {
38
+ debugMessage(`ConnectionProfileOps.findConnectionProfiles: '${host}' identifies '${tenant}', including in result set`);
37
39
  const foundProfile = {
38
40
  ...connectionProfiles[tenant]
39
41
  };
@@ -93,7 +93,8 @@ argv:
93
93
  */
94
94
  function getFrodoCommand() {
95
95
  try {
96
- if (!process.argv[1].endsWith('frodo') && !process.argv[1].endsWith('frodo.exe')) {
96
+ if (mode !== MODES.RECORD) debugMessage(`SetupPollyForFrodoLib.getFrodoCommand: process.argv=${process.argv}`);
97
+ if (!process.argv[1].endsWith('frodo') && !process.argv[1].endsWith('frodo.exe') && !process.argv[1].endsWith('app.js')) {
97
98
  return path.parse(process.argv[1]).name.replace('-', '/');
98
99
  }
99
100
  return process.argv[2];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rockcarver/frodo-lib",
3
- "version": "0.18.7",
3
+ "version": "0.18.9-0",
4
4
  "type": "commonjs",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.mjs",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ops/AuthenticateOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,MAAM,EAAwB,MAAM,WAAW,CAAC;AA0TzD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,gBAAgB,EAAE,MAAM,EACxB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;AAmCD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,gBAAgB,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAsH1E","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 { getServiceAccount } from './cloud/ServiceAccountOps';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst cloudIdmAdminScopes = 'openid fr:idm:* fr:idc:esv:*';\nconst forgeopsIdmAdminScopes = 'openid fr:idm:*';\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=${cloudIdmAdminScopes}&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=${forgeopsIdmAdminScopes}&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=${\n state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY\n ? cloudIdmAdminScopes\n : forgeopsIdmAdminScopes\n }&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 debugMessage(`AuthenticateOps.getAccessTokenForUser: start`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end with token`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end without token`);\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();\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 = (await getServiceAccount(state.getServiceAccountId())).name;\n subjectString = `service account ${name} [${state.getServiceAccountId()}]`;\n }\n return subjectString;\n}\n\n/**\n * Get tokens\n * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false)\n * @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(forceLoginAsUser = false): Promise<boolean> {\n debugMessage(`AuthenticateOps.getTokens: start`);\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 (\n !forceLoginAsUser &&\n state.getServiceAccountId() &&\n state.getServiceAccountJwk()\n ) {\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 debugMessage(saErr.response?.data || saErr);\n throw new Error(\n `Service account login error: ${\n saErr.response?.data?.error_description ||\n saErr.response?.data?.message ||\n saErr\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 debugMessage(`AuthenticateOps.getTokens: end with tokens`);\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 debugMessage(`AuthenticateOps.getTokens: end without tokens`);\n return false;\n}\n"]}
1
+ {"version":3,"sources":["../src/ops/AuthenticateOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,MAAM,EAAwB,MAAM,WAAW,CAAC;AAqWzD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,gBAAgB,EAAE,MAAM,EACxB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;AAmCD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,gBAAgB,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAiI1E","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 { getServiceAccount } from './cloud/ServiceAccountOps';\nimport { isValidUrl } from './utils/OpsUtils';\n\nconst adminClientPassword = 'doesnotmatter';\nconst redirectUrlTemplate = '/platform/appAuthHelperRedirect.html';\n\nconst cloudIdmAdminScopes = 'openid fr:idm:* fr:idc:esv:*';\nconst forgeopsIdmAdminScopes = 'openid fr:idm:*';\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 debugMessage(`AuthenticateOps.checkAndHandle2FA: start`);\n debugMessage(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 debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]`\n );\n return {\n need2fa: true,\n factor: 'None',\n supported: true,\n payload,\n };\n }\n if (element.input[0].value.includes('webAuthnOutcome')) {\n // webauthn!!!\n debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]`\n );\n return {\n need2fa: true,\n factor: 'WebAuthN',\n supported: false,\n payload,\n };\n }\n }\n if (element.type === 'NameCallback') {\n if (element.output[0].value.includes('code')) {\n // skippable = false;\n debugMessage(\n `AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false`\n );\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 debugMessage(\n `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]`\n );\n return {\n need2fa: true,\n factor: 'Code',\n supported: true,\n payload,\n };\n }\n }\n }\n debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);\n return {\n need2fa: false,\n factor: 'None',\n supported: true,\n payload,\n };\n }\n debugMessage(`AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`);\n return {\n need2fa: false,\n factor: 'None',\n supported: true,\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=${cloudIdmAdminScopes}&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=${forgeopsIdmAdminScopes}&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 debugMessage(`AuthenticateOps.authenticate: start`);\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\n // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN)\n if (!skip2FA.supported) {\n throw new Error(`Unsupported 2FA factor: ${skip2FA.factor}`);\n }\n\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 debugMessage(\n `AuthenticateOps.authenticate: end [tokenId=${response2['tokenId']}]`\n );\n return response2['tokenId'] as string;\n }\n debugMessage(`AuthenticateOps.authenticate: end [no session]`);\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=${\n state.getDeploymentType() === globalConfig.CLOUD_DEPLOYMENT_TYPE_KEY\n ? cloudIdmAdminScopes\n : forgeopsIdmAdminScopes\n }&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 debugMessage(`AuthenticateOps.getAccessTokenForUser: start`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end with token`);\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 debugMessage(`AuthenticateOps.getAccessTokenForUser: end without token`);\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();\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 = (await getServiceAccount(state.getServiceAccountId())).name;\n subjectString = `service account ${name} [${state.getServiceAccountId()}]`;\n }\n return subjectString;\n}\n\n/**\n * Get tokens\n * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false)\n * @returns {Promise<boolean>} true if tokens were successfully obtained, false otherwise\n */\nexport async function getTokens(forceLoginAsUser = false): Promise<boolean> {\n debugMessage(`AuthenticateOps.getTokens: start`);\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\n // if host is not a valid URL, try to locate a valid URL from connections.json\n if (!isValidUrl(state.getHost())) {\n const conn = await getConnectionProfile();\n if (conn) {\n state.setHost(conn.tenant);\n } else {\n return false;\n }\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 (\n !forceLoginAsUser &&\n state.getServiceAccountId() &&\n state.getServiceAccountJwk()\n ) {\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 debugMessage(saErr.response?.data || saErr);\n throw new Error(\n `Service account login error: ${\n saErr.response?.data?.error_description ||\n saErr.response?.data?.message ||\n saErr\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 debugMessage(`AuthenticateOps.getTokens: end with tokens`);\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 debugMessage(`AuthenticateOps.getTokens: end without tokens`);\n return false;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ops/ConnectionProfileOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,MAAM,EAAE,MAAM,WAAW,CAAC;AAK9E,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,QAsClD;AAyBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,kBAiD3C;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,0BAA0B,CAAC,CAiDrC;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,CA2G1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,iBAqDrB;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 {\n createServiceAccount,\n getServiceAccount,\n} from './cloud/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 (Object.keys(connectionsData).length < 1) {\n printMessage(`No connections defined yet in ${filename}`, 'info');\n } else {\n if (long) {\n const table = createTable([\n 'Host',\n 'Service Account',\n 'Username',\n 'Log API Key',\n ]);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].svcacctName || connectionsData[c].svcacctId,\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 // getUniqueNames(5, Object.keys(connectionsData));\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 }\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 const newPath = `${os.homedir()}/.frodo/${newProfileFilename}`;\n if (!fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // no connections file (old or new), create empty new one\n fs.writeFileSync(\n newPath,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n } else if (fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // old exists, new one does not - so copy old to new one\n fs.copyFileSync(legacyPath, newPath);\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 // in other cases, where\n // (both old and new exist) OR (only new one exists) don't do anything\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 svcacctName: profiles[0].svcacctName ? profiles[0].svcacctName : null,\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: start`);\n const filename = getConnectionProfilesPath();\n debugMessage(`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 debugMessage(profile);\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 debugMessage(`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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [false]`);\n return false;\n }\n }\n } catch (error) {\n debugMessage(`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 profile.svcacctName = (\n await getServiceAccount(state.getServiceAccountId())\n ).name;\n }\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n // update existing service account profile\n if (profile.svcacctId && !profile.svcacctName) {\n profile.svcacctName = (await getServiceAccount(profile.svcacctId)).name;\n debugMessage(\n `ConnectionProfileOps.saveConnectionProfile: added missing service account name`\n );\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [true]`);\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: start`);\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n debugMessage(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 delete profile.svcacctName;\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 svcacctName: 'Service Account Name',\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: end`);\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
+ {"version":3,"sources":["../src/ops/ConnectionProfileOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,MAAM,EAAE,MAAM,WAAW,CAAC;AAK9E,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;AA6BD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,UAAQ,QAsClD;AAyBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,kBAiD3C;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,0BAA0B,CAAC,CAiDrC;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,CA2G1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,iBAqDrB;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 {\n createServiceAccount,\n getServiceAccount,\n} from './cloud/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 debugMessage(\n `ConnectionProfileOps.findConnectionProfiles: tenant=${tenant}`\n );\n if (tenant.includes(host)) {\n debugMessage(\n `ConnectionProfileOps.findConnectionProfiles: '${host}' identifies '${tenant}', including in result set`\n );\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 (Object.keys(connectionsData).length < 1) {\n printMessage(`No connections defined yet in ${filename}`, 'info');\n } else {\n if (long) {\n const table = createTable([\n 'Host',\n 'Service Account',\n 'Username',\n 'Log API Key',\n ]);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].svcacctName || connectionsData[c].svcacctId,\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 // getUniqueNames(5, Object.keys(connectionsData));\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 }\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 const newPath = `${os.homedir()}/.frodo/${newProfileFilename}`;\n if (!fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // no connections file (old or new), create empty new one\n fs.writeFileSync(\n newPath,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n } else if (fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {\n // old exists, new one does not - so copy old to new one\n fs.copyFileSync(legacyPath, newPath);\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 // in other cases, where\n // (both old and new exist) OR (only new one exists) don't do anything\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 svcacctName: profiles[0].svcacctName ? profiles[0].svcacctName : null,\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: start`);\n const filename = getConnectionProfilesPath();\n debugMessage(`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 debugMessage(profile);\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 debugMessage(`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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [false]`);\n return false;\n }\n }\n } catch (error) {\n debugMessage(`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 profile.svcacctName = (\n await getServiceAccount(state.getServiceAccountId())\n ).name;\n }\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n // update existing service account profile\n if (profile.svcacctId && !profile.svcacctName) {\n profile.svcacctName = (await getServiceAccount(profile.svcacctId)).name;\n debugMessage(\n `ConnectionProfileOps.saveConnectionProfile: added missing service account name`\n );\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 debugMessage(`ConnectionProfileOps.saveConnectionProfile: end [true]`);\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: start`);\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n debugMessage(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 delete profile.svcacctName;\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 svcacctName: 'Service Account Name',\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 debugMessage(`ConnectionProfileOps.describeConnectionProfile: end`);\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 +1 @@
1
- {"version":3,"sources":["../src/utils/SetupPollyForFrodoLib.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AA8HtC,wBAAgB,qBAAqB,CACnC,eAAe,MAA2B,GACzC,KAAK,CAsEP","file":"SetupPollyForFrodoLib.d.ts","sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Polly } from '@pollyjs/core';\nimport { MODES } from '@pollyjs/utils';\nimport NodeHttpAdapter from '@pollyjs/adapter-node-http';\nimport FSPersister from '@pollyjs/persister-fs';\nimport { LogLevelDesc } from 'loglevel';\nimport { debugMessage, printMessage } from '../ops/utils/Console';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst FRODO_MOCK_HOSTS = [\n 'https://openam-frodo-dev.forgeblocks.com',\n 'https://openam-service-accounts.forgeblocks.com',\n 'https://openam-volker-dev.forgeblocks.com',\n];\n\nlet recordIfMissing = false;\nlet mode = MODES.REPLAY;\n\n// resolve \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/esm/api\" to\n// \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/src/test/recordings\"\nconst recordingsDir = __dirname.replace(\n /^(.*\\/frodo-\\w{3})(.*)$/gi,\n '$1/mocks'\n);\n\nif (process.env.FRODO_MOCK) {\n Polly.register(NodeHttpAdapter);\n Polly.register(FSPersister);\n if (process.env.FRODO_MOCK === 'record') {\n mode = MODES.RECORD;\n recordIfMissing = true;\n }\n}\n\nfunction defaultMatchRequestsBy() {\n return JSON.parse(\n JSON.stringify({\n method: true,\n headers: false, // do not match headers, because \"Authorization\" header is sent only at recording time\n body: true,\n order: false,\n url: {\n protocol: false,\n username: false,\n password: false,\n hostname: false, // we will record from different envs but run tests always against `frodo-dev`\n port: false,\n pathname: true,\n query: true,\n hash: true,\n },\n })\n );\n}\n\nfunction authenticationMatchRequestsBy() {\n const matchRequestsBy = defaultMatchRequestsBy();\n matchRequestsBy.body = false;\n matchRequestsBy.order = true;\n return matchRequestsBy;\n}\n\n// returns a delayed promise\nasync function delay(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function countdown(ms) {\n await delay(ms);\n return --ttl;\n}\n\nconst timeout = 15;\nlet ttl = timeout;\nasync function scheduleShutdown(polly: Polly) {\n ++ttl;\n while (await countdown(1000)) {\n if (ttl < 4)\n console.log(\n `Polly instance '${getFrodoCommand()}' stopping in ${ttl}s...`\n );\n }\n await polly.stop();\n console.log(`Polly instance '${getFrodoCommand()}' stopped.`);\n}\n\n/*\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/usr/local/bin/frodo',\n 'journey',\n 'list',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/Users/vscheuber/Projects/frodo-cli/esm/cli/journey/journey-list.js',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\n*/\nfunction getFrodoCommand() {\n try {\n if (\n !process.argv[1].endsWith('frodo') &&\n !process.argv[1].endsWith('frodo.exe')\n ) {\n return path.parse(process.argv[1]).name.replace('-', '/');\n }\n return process.argv[2];\n } catch (error) {\n printMessage(`SetupPollyForFrodoLib.getFrodoCommand: ${error}`, 'error');\n printMessage(process.argv, 'error');\n return 'error';\n }\n}\n\nexport function setupPollyForFrodoLib(\n matchRequestsBy = defaultMatchRequestsBy()\n): Polly {\n const polly = new Polly('default');\n\n polly.configure({\n adapters: ['node-http'],\n mode,\n recordIfMissing,\n flushRequestsOnStop: true,\n logLevel: (process.env.FRODO_POLLY_LOG_LEVEL as LogLevelDesc) || 'warn',\n recordFailedRequests: true,\n persister: 'fs',\n persisterOptions: {\n fs: {\n recordingsDir,\n },\n },\n matchRequestsBy,\n });\n\n for (const host of FRODO_MOCK_HOSTS) {\n if (mode === MODES.RECORD) console.log(`***** Host: ${host}`);\n polly.server.host(host, () => {\n polly.server\n .any('/am/oauth2/*')\n .recordingName(`${getFrodoCommand()}/oauth2`)\n .on('request', (req) => {\n req.configure({ matchRequestsBy: authenticationMatchRequestsBy() });\n });\n polly.server.any('/am/json/*').recordingName(`${getFrodoCommand()}/am`);\n polly.server\n .any('/openidm/*')\n .recordingName(`${getFrodoCommand()}/openidm`);\n polly.server\n .any('/environment/*')\n .recordingName(`${getFrodoCommand()}/environment`);\n polly.server\n .any('/monitoring/*')\n .recordingName(`${getFrodoCommand()}/monitoring`);\n polly.server\n .any('/feature')\n .recordingName(`${getFrodoCommand()}/feature`);\n polly.server\n .any('/dashboard/*')\n .recordingName(`${getFrodoCommand()}/dashboard`);\n });\n }\n polly.server.host('https://api.github.com', () => {\n polly.server.any('/*').recordingName(`github`);\n });\n polly.server.host('https://registry.npmjs.org', () => {\n polly.server.any('/*').recordingName(`npmjs`);\n });\n polly.server.any().on('request', () => {\n if (ttl < timeout) {\n // console.log(`Reset polly stop ttl (${ttl}) to ${timeout}`);\n ttl = timeout;\n }\n });\n\n if (mode === MODES.RECORD) {\n scheduleShutdown(polly);\n } else {\n // only output debug messages if not recording as this polly instance is\n // primarily used by frodo-cli e2e tests, which capture stdout in snapshots.\n // debug messages falsify the snapshot recordings.\n debugMessage(`Polly config:`);\n debugMessage(polly.config);\n }\n\n return polly;\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/SetupPollyForFrodoLib.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAmItC,wBAAgB,qBAAqB,CACnC,eAAe,MAA2B,GACzC,KAAK,CAsEP","file":"SetupPollyForFrodoLib.d.ts","sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Polly } from '@pollyjs/core';\nimport { MODES } from '@pollyjs/utils';\nimport NodeHttpAdapter from '@pollyjs/adapter-node-http';\nimport FSPersister from '@pollyjs/persister-fs';\nimport { LogLevelDesc } from 'loglevel';\nimport { debugMessage, printMessage } from '../ops/utils/Console';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst FRODO_MOCK_HOSTS = [\n 'https://openam-frodo-dev.forgeblocks.com',\n 'https://openam-service-accounts.forgeblocks.com',\n 'https://openam-volker-dev.forgeblocks.com',\n];\n\nlet recordIfMissing = false;\nlet mode = MODES.REPLAY;\n\n// resolve \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/esm/api\" to\n// \"/home/sandeepc/work/ForgeRock/sources/frodo-lib/src/test/recordings\"\nconst recordingsDir = __dirname.replace(\n /^(.*\\/frodo-\\w{3})(.*)$/gi,\n '$1/mocks'\n);\n\nif (process.env.FRODO_MOCK) {\n Polly.register(NodeHttpAdapter);\n Polly.register(FSPersister);\n if (process.env.FRODO_MOCK === 'record') {\n mode = MODES.RECORD;\n recordIfMissing = true;\n }\n}\n\nfunction defaultMatchRequestsBy() {\n return JSON.parse(\n JSON.stringify({\n method: true,\n headers: false, // do not match headers, because \"Authorization\" header is sent only at recording time\n body: true,\n order: false,\n url: {\n protocol: false,\n username: false,\n password: false,\n hostname: false, // we will record from different envs but run tests always against `frodo-dev`\n port: false,\n pathname: true,\n query: true,\n hash: true,\n },\n })\n );\n}\n\nfunction authenticationMatchRequestsBy() {\n const matchRequestsBy = defaultMatchRequestsBy();\n matchRequestsBy.body = false;\n matchRequestsBy.order = true;\n return matchRequestsBy;\n}\n\n// returns a delayed promise\nasync function delay(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function countdown(ms) {\n await delay(ms);\n return --ttl;\n}\n\nconst timeout = 15;\nlet ttl = timeout;\nasync function scheduleShutdown(polly: Polly) {\n ++ttl;\n while (await countdown(1000)) {\n if (ttl < 4)\n console.log(\n `Polly instance '${getFrodoCommand()}' stopping in ${ttl}s...`\n );\n }\n await polly.stop();\n console.log(`Polly instance '${getFrodoCommand()}' stopped.`);\n}\n\n/*\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/usr/local/bin/frodo',\n 'journey',\n 'list',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\nargv:\n[\n '/Users/vscheuber/.nvm/versions/node/v18.7.0/bin/node',\n '/Users/vscheuber/Projects/frodo-cli/esm/cli/journey/journey-list.js',\n '-l',\n 'https://openam-volker-dev.forgeblocks.com/am',\n 'alpha',\n 'volker.scheuber@forgerock.com',\n 'Sup3rS3cr3t!'\n]\n*/\nfunction getFrodoCommand() {\n try {\n if (mode !== MODES.RECORD)\n debugMessage(\n `SetupPollyForFrodoLib.getFrodoCommand: process.argv=${process.argv}`\n );\n if (\n !process.argv[1].endsWith('frodo') &&\n !process.argv[1].endsWith('frodo.exe') &&\n !process.argv[1].endsWith('app.js')\n ) {\n return path.parse(process.argv[1]).name.replace('-', '/');\n }\n return process.argv[2];\n } catch (error) {\n printMessage(`SetupPollyForFrodoLib.getFrodoCommand: ${error}`, 'error');\n printMessage(process.argv, 'error');\n return 'error';\n }\n}\n\nexport function setupPollyForFrodoLib(\n matchRequestsBy = defaultMatchRequestsBy()\n): Polly {\n const polly = new Polly('default');\n\n polly.configure({\n adapters: ['node-http'],\n mode,\n recordIfMissing,\n flushRequestsOnStop: true,\n logLevel: (process.env.FRODO_POLLY_LOG_LEVEL as LogLevelDesc) || 'warn',\n recordFailedRequests: true,\n persister: 'fs',\n persisterOptions: {\n fs: {\n recordingsDir,\n },\n },\n matchRequestsBy,\n });\n\n for (const host of FRODO_MOCK_HOSTS) {\n if (mode === MODES.RECORD) console.log(`***** Host: ${host}`);\n polly.server.host(host, () => {\n polly.server\n .any('/am/oauth2/*')\n .recordingName(`${getFrodoCommand()}/oauth2`)\n .on('request', (req) => {\n req.configure({ matchRequestsBy: authenticationMatchRequestsBy() });\n });\n polly.server.any('/am/json/*').recordingName(`${getFrodoCommand()}/am`);\n polly.server\n .any('/openidm/*')\n .recordingName(`${getFrodoCommand()}/openidm`);\n polly.server\n .any('/environment/*')\n .recordingName(`${getFrodoCommand()}/environment`);\n polly.server\n .any('/monitoring/*')\n .recordingName(`${getFrodoCommand()}/monitoring`);\n polly.server\n .any('/feature')\n .recordingName(`${getFrodoCommand()}/feature`);\n polly.server\n .any('/dashboard/*')\n .recordingName(`${getFrodoCommand()}/dashboard`);\n });\n }\n polly.server.host('https://api.github.com', () => {\n polly.server.any('/*').recordingName(`github`);\n });\n polly.server.host('https://registry.npmjs.org', () => {\n polly.server.any('/*').recordingName(`npmjs`);\n });\n polly.server.any().on('request', () => {\n if (ttl < timeout) {\n // console.log(`Reset polly stop ttl (${ttl}) to ${timeout}`);\n ttl = timeout;\n }\n });\n\n if (mode === MODES.RECORD) {\n scheduleShutdown(polly);\n } else {\n // only output debug messages if not recording as this polly instance is\n // primarily used by frodo-cli e2e tests, which capture stdout in snapshots.\n // debug messages falsify the snapshot recordings.\n debugMessage(`Polly config:`);\n debugMessage(polly.config);\n }\n\n return polly;\n}\n"]}