@rockcarver/frodo-lib 0.18.0 → 0.18.1-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,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.18.1-0] - 2023-01-16
11
+
10
12
  ## [0.18.0] - 2023-01-13
11
13
 
12
14
  ### Added
@@ -1003,7 +1005,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1003
1005
  - Fixed problem with adding connection profiles
1004
1006
  - Miscellaneous bug fixes
1005
1007
 
1006
- [Unreleased]: https://github.com/rockcarver/frodo-lib/compare/v0.18.0...HEAD
1008
+ [Unreleased]: https://github.com/rockcarver/frodo-lib/compare/v0.18.1-0...HEAD
1009
+
1010
+ [0.18.1-0]: https://github.com/rockcarver/frodo-lib/compare/v0.18.0...v0.18.1-0
1007
1011
 
1008
1012
  [0.18.0]: https://github.com/rockcarver/frodo-lib/compare/v0.17.8-3...v0.18.0
1009
1013
 
@@ -74,18 +74,24 @@ function listConnectionProfiles() {
74
74
  try {
75
75
  var data = _fs.default.readFileSync(filename, 'utf8');
76
76
  var connectionsData = JSON.parse(data);
77
- if (long) {
78
- var table = (0, _Console.createTable)(['Host', 'Username', 'Log API Key']);
79
- Object.keys(connectionsData).forEach(c => {
80
- table.push([c, connectionsData[c].username, connectionsData[c].logApiKey]);
81
- });
82
- (0, _Console.printMessage)(table.toString(), 'data');
77
+ if (Object.keys(connectionsData).length < 1) {
78
+ (0, _Console.printMessage)("No connections defined yet in ".concat(filename), 'info');
83
79
  } else {
84
- Object.keys(connectionsData).forEach(c => {
85
- (0, _Console.printMessage)("".concat(c), 'data');
86
- });
80
+ if (long) {
81
+ var table = (0, _Console.createTable)(['Host', 'Username', 'Log API Key']);
82
+ Object.keys(connectionsData).forEach(c => {
83
+ table.push([c, connectionsData[c].username, connectionsData[c].logApiKey]);
84
+ });
85
+ (0, _Console.printMessage)(table.toString(), 'data');
86
+ } else {
87
+ Object.keys(connectionsData).forEach(c => {
88
+ (0, _Console.printMessage)("".concat(c), 'data');
89
+ });
90
+ // getUniqueNames(5, Object.keys(connectionsData));
91
+ }
92
+
93
+ (0, _Console.printMessage)('Any unique substring of a saved host can be used as the value for host parameter in all commands', 'info');
87
94
  }
88
- (0, _Console.printMessage)('Any unique substring of a saved host can be used as the value for host parameter in all commands', 'info');
89
95
  } catch (e) {
90
96
  (0, _Console.printMessage)("No connections found in ".concat(filename, " (").concat(e.message, ")"), 'error');
91
97
  }
@@ -96,12 +102,19 @@ function listConnectionProfiles() {
96
102
  */
97
103
  function migrateFromLegacyProfile() {
98
104
  var legacyPath = "".concat(_os.default.homedir(), "/.frodo/").concat(legacyProfileFilename);
99
- if (_fs.default.existsSync(legacyPath)) {
100
- _fs.default.copyFileSync(legacyPath, "".concat(_os.default.homedir(), "/.frodo/").concat(newProfileFilename));
105
+ var newPath = "".concat(_os.default.homedir(), "/.frodo/").concat(newProfileFilename);
106
+ if (!_fs.default.existsSync(legacyPath) && !_fs.default.existsSync(newPath)) {
107
+ // no connections file (old or new), create empty new one
108
+ _fs.default.writeFileSync(newPath, JSON.stringify({}, null, fileOptions.indentation));
109
+ } else if (_fs.default.existsSync(legacyPath) && !_fs.default.existsSync(newPath)) {
110
+ // old exists, new one does not - so copy old to new one
111
+ _fs.default.copyFileSync(legacyPath, newPath);
101
112
  // for now, just add a "deprecated" suffix. May delete the old file
102
113
  // in a future release
103
114
  _fs.default.renameSync(legacyPath, "".concat(legacyPath, ".deprecated"));
104
115
  }
116
+ // in other cases, where
117
+ // (both old and new exist) OR (only new one exists) don't do anything
105
118
  }
106
119
 
107
120
  /**
@@ -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","table","createTable","Object","keys","forEach","c","username","logApiKey","printMessage","toString","e","message","migrateFromLegacyProfile","legacyPath","existsSync","copyFileSync","renameSync","initConnectionProfiles","folderName","path","dirname","mkdirSync","recursive","writeFileSync","stringify","convert","conn","encodedPassword","encrypt","encodedLogApiSecret","encodedSvcacctJwk","getConnectionProfileByHost","length","p","password","decrypt","logApiSecret","authenticationService","authenticationHeaderOverrides","svcacctId","svcacctJwk","getConnectionProfile","getHost","saveConnectionProfile","verboseMessage","profile","statSync","found","setHost","isValidUrl","error","getUsername","getPassword","getLogApiKey","getLogApiSecret","getServiceAccountId","getServiceAccountJwk","getAuthenticationService","getAuthenticationHeaderOverrides","entries","orderedProfiles","sort","reduce","obj","key","saveJsonToFile","deleteConnectionProfile","stat","err","code","describeConnectionProfile","showSecrets","present","jwk","keyMap","createObjectTable","addNewServiceAccount","debugMessage","name","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 { createServiceAccount } 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 (long) {\n const table = createTable(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n }\n printMessage(\n 'Any unique substring of a saved host can be used as the value for host parameter in all commands',\n 'info'\n );\n } catch (e) {\n printMessage(`No connections found in ${filename} (${e.message})`, 'error');\n }\n}\n\n/**\n * Migrate from .frodorc to Connections.json\n */\nfunction migrateFromLegacyProfile() {\n const legacyPath = `${os.homedir()}/.frodo/${legacyProfileFilename}`;\n if (fs.existsSync(legacyPath)) {\n fs.copyFileSync(legacyPath, `${os.homedir()}/.frodo/${newProfileFilename}`);\n // for now, just add a \"deprecated\" suffix. May delete the old file\n // in a future release\n fs.renameSync(legacyPath, `${legacyPath}.deprecated`);\n }\n}\n\n/**\n * Initialize connection profiles\n *\n * This method is called from app.ts and runs before any of the message handlers are registered.\n * Therefore none of the Console message functions will produce any output.\n */\nexport async function initConnectionProfiles() {\n // create connections.json file if it doesn't exist\n const filename = getConnectionProfilesPath();\n const folderName = path.dirname(filename);\n if (!fs.existsSync(folderName)) {\n fs.mkdirSync(folderName, { recursive: true });\n if (!fs.existsSync(filename)) {\n fs.writeFileSync(\n filename,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n }\n }\n // encrypt the password and logApiSecret from clear text to aes-256-GCM\n else {\n migrateFromLegacyProfile();\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData: ConnectionsFileInterface = JSON.parse(data);\n let convert = false;\n for (const conn of Object.keys(connectionsData)) {\n if (connectionsData[conn]['password']) {\n convert = true;\n connectionsData[conn].encodedPassword = await crypto.encrypt(\n connectionsData[conn]['password']\n );\n delete connectionsData[conn]['password'];\n }\n if (connectionsData[conn]['logApiSecret']) {\n convert = true;\n connectionsData[conn].encodedLogApiSecret = await crypto.encrypt(\n connectionsData[conn]['logApiSecret']\n );\n delete connectionsData[conn]['logApiSecret'];\n }\n if (connectionsData[conn]['svcacctJwk']) {\n convert = true;\n connectionsData[conn].encodedSvcacctJwk = await crypto.encrypt(\n connectionsData[conn]['svcacctJwk']\n );\n delete connectionsData[conn]['svcacctJwk'];\n }\n }\n if (convert) {\n fs.writeFileSync(\n filename,\n JSON.stringify(connectionsData, null, fileOptions.indentation)\n );\n }\n }\n}\n\n/**\n * Get connection profile by host\n * @param {String} host host tenant host url or unique substring\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfileByHost(\n host: string\n): Promise<ConnectionProfileInterface> {\n try {\n const filename = getConnectionProfilesPath();\n const connectionsData = JSON.parse(fs.readFileSync(filename, 'utf8'));\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 0) {\n printMessage(\n `Profile for ${host} not found. Please specify credentials on command line`,\n 'error'\n );\n return null;\n }\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n }\n return {\n tenant: profiles[0].tenant,\n username: profiles[0].username ? profiles[0].username : null,\n password: profiles[0].encodedPassword\n ? await crypto.decrypt(profiles[0].encodedPassword)\n : null,\n logApiKey: profiles[0].logApiKey ? profiles[0].logApiKey : null,\n logApiSecret: profiles[0].encodedLogApiSecret\n ? await crypto.decrypt(profiles[0].encodedLogApiSecret)\n : null,\n authenticationService: profiles[0].authenticationService\n ? profiles[0].authenticationService\n : null,\n authenticationHeaderOverrides: profiles[0].authenticationHeaderOverrides\n ? profiles[0].authenticationHeaderOverrides\n : {},\n svcacctId: profiles[0].svcacctId ? profiles[0].svcacctId : null,\n svcacctJwk: profiles[0].encodedSvcacctJwk\n ? await crypto.decrypt(profiles[0].encodedSvcacctJwk)\n : null,\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile(): Promise<ConnectionProfileInterface> {\n return getConnectionProfileByHost(state.getHost());\n}\n\n/**\n * Save connection profile\n * @param {string} host host url for new profiles or unique substring for existing profiles\n * @returns {Promise<boolean>} true if the operation succeeded, false otherwise\n */\nexport async function saveConnectionProfile(host: string): Promise<boolean> {\n const filename = getConnectionProfilesPath();\n verboseMessage(`Saving connection profile in ${filename}`);\n let profiles: ConnectionsFileInterface = {};\n let profile: SecureConnectionProfileInterface = { tenant: '' };\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n profiles = JSON.parse(data);\n\n // find tenant\n const found = findConnectionProfiles(profiles, host);\n\n // replace tenant in session with real tenant url if necessary\n if (found.length === 1) {\n profile = found[0];\n state.setHost(profile.tenant);\n verboseMessage(`Existing profile: ${profile.tenant}`);\n }\n\n // connection profile not found, validate host is a real URL\n if (found.length === 0) {\n if (isValidUrl(host)) {\n state.setHost(host);\n verboseMessage(`New profile: ${host}`);\n } else {\n printMessage(\n `No existing profile found matching '${host}'. Provide a valid URL as the host argument to create a new profile.`,\n 'error'\n );\n return false;\n }\n }\n } catch (error) {\n verboseMessage(`New profiles file ${filename} with new profile ${host}`);\n }\n\n // user account\n if (state.getUsername()) profile.username = state.getUsername();\n if (state.getPassword())\n profile.encodedPassword = await crypto.encrypt(state.getPassword());\n\n // log API\n if (state.getLogApiKey()) profile.logApiKey = state.getLogApiKey();\n if (state.getLogApiSecret())\n profile.encodedLogApiSecret = await crypto.encrypt(state.getLogApiSecret());\n\n // service account\n if (state.getServiceAccountId())\n profile.svcacctId = state.getServiceAccountId();\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n\n // advanced settings\n if (state.getAuthenticationService()) {\n profile.authenticationService = state.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n state.getAuthenticationService(),\n 'info'\n );\n }\n if (\n state.getAuthenticationHeaderOverrides() &&\n Object.entries(state.getAuthenticationHeaderOverrides()).length\n ) {\n profile.authenticationHeaderOverrides =\n state.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(state.getAuthenticationHeaderOverrides(), 'info');\n }\n\n // remove the helper key 'tenant'\n delete profile.tenant;\n\n // update profiles\n profiles[state.getHost()] = profile;\n\n // sort profiles\n const orderedProfiles = Object.keys(profiles)\n .sort()\n .reduce((obj, key) => {\n obj[key] = profiles[key];\n return obj;\n }, {});\n\n // save profiles\n saveJsonToFile(orderedProfiles, filename, false);\n verboseMessage(`Saved connection profile ${state.getHost()} in ${filename}`);\n return true;\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData: ConnectionsFileInterface = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 1) {\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n printMessage(`Deleted connection profile ${profiles[0].tenant}`);\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\n/**\n * Describe connection profile\n * @param {string} host Host URL or unique substring\n * @param {boolean} showSecrets Whether secrets should be shown in clear text or not\n */\nexport async function describeConnectionProfile(\n host: string,\n showSecrets: boolean\n) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n const present = '[present]';\n const jwk = profile.svcacctJwk;\n if (!showSecrets) {\n if (profile.password) profile.password = present;\n if (profile.logApiSecret) profile.logApiSecret = present;\n if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;\n }\n if (!profile.username) {\n delete profile.username;\n delete profile.password;\n }\n if (!profile.logApiKey) {\n delete profile.logApiKey;\n delete profile.logApiSecret;\n }\n if (!profile.svcacctId) {\n delete profile.svcacctId;\n delete profile.svcacctJwk;\n }\n if (showSecrets && jwk) {\n (profile as unknown)['svcacctJwk'] = 'see below';\n }\n if (!profile.authenticationService) {\n delete profile.authenticationService;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n logApiKey: 'Log API Key',\n logApiSecret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n svcacctId: 'Service Account Id',\n svcacctJwk: 'Service Account JWK',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n if (showSecrets && jwk) {\n printMessage(jwk, 'data');\n }\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n\n/**\n * Create a new service account using auto-generated parameters\n * @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object\n */\nexport async function addNewServiceAccount(): Promise<ObjectSkeletonInterface> {\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: start`);\n const name = `Frodo-SA-${new Date().getTime()}`;\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: name=${name}...`);\n const description = `${state.getUsername()}'s Frodo Service Account`;\n const scope = ['fr:am:*', 'fr:idm:*', 'fr:idc:esv:*'];\n const jwkPrivate = await createJwkRsa();\n const jwkPublic = await getJwkRsaPublic(jwkPrivate);\n const jwks = createJwks(jwkPublic);\n const sa = await createServiceAccount(\n name,\n description,\n 'Active',\n scope,\n jwks\n );\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: id=${sa._id}`);\n state.setServiceAccountId(sa._id);\n state.setServiceAccountJwk(jwkPrivate);\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: end`);\n return sa;\n}\n"],"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","includes","foundProfile","push","listConnectionProfiles","long","filename","data","fs","readFileSync","connectionsData","JSON","parse","Object","keys","length","printMessage","table","createTable","forEach","c","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","svcacctId","svcacctJwk","getConnectionProfile","getHost","saveConnectionProfile","verboseMessage","profile","statSync","found","setHost","isValidUrl","error","getUsername","getPassword","getLogApiKey","getLogApiSecret","getServiceAccountId","getServiceAccountJwk","getAuthenticationService","getAuthenticationHeaderOverrides","entries","orderedProfiles","sort","reduce","obj","key","saveJsonToFile","deleteConnectionProfile","stat","err","code","describeConnectionProfile","showSecrets","present","jwk","keyMap","createObjectTable","addNewServiceAccount","debugMessage","name","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 { createServiceAccount } 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(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n // 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 svcacctId: profiles[0].svcacctId ? profiles[0].svcacctId : null,\n svcacctJwk: profiles[0].encodedSvcacctJwk\n ? await crypto.decrypt(profiles[0].encodedSvcacctJwk)\n : null,\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile(): Promise<ConnectionProfileInterface> {\n return getConnectionProfileByHost(state.getHost());\n}\n\n/**\n * Save connection profile\n * @param {string} host host url for new profiles or unique substring for existing profiles\n * @returns {Promise<boolean>} true if the operation succeeded, false otherwise\n */\nexport async function saveConnectionProfile(host: string): Promise<boolean> {\n const filename = getConnectionProfilesPath();\n verboseMessage(`Saving connection profile in ${filename}`);\n let profiles: ConnectionsFileInterface = {};\n let profile: SecureConnectionProfileInterface = { tenant: '' };\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n profiles = JSON.parse(data);\n\n // find tenant\n const found = findConnectionProfiles(profiles, host);\n\n // replace tenant in session with real tenant url if necessary\n if (found.length === 1) {\n profile = found[0];\n state.setHost(profile.tenant);\n verboseMessage(`Existing profile: ${profile.tenant}`);\n }\n\n // connection profile not found, validate host is a real URL\n if (found.length === 0) {\n if (isValidUrl(host)) {\n state.setHost(host);\n verboseMessage(`New profile: ${host}`);\n } else {\n printMessage(\n `No existing profile found matching '${host}'. Provide a valid URL as the host argument to create a new profile.`,\n 'error'\n );\n return false;\n }\n }\n } catch (error) {\n verboseMessage(`New profiles file ${filename} with new profile ${host}`);\n }\n\n // user account\n if (state.getUsername()) profile.username = state.getUsername();\n if (state.getPassword())\n profile.encodedPassword = await crypto.encrypt(state.getPassword());\n\n // log API\n if (state.getLogApiKey()) profile.logApiKey = state.getLogApiKey();\n if (state.getLogApiSecret())\n profile.encodedLogApiSecret = await crypto.encrypt(state.getLogApiSecret());\n\n // service account\n if (state.getServiceAccountId())\n profile.svcacctId = state.getServiceAccountId();\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n\n // advanced settings\n if (state.getAuthenticationService()) {\n profile.authenticationService = state.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n state.getAuthenticationService(),\n 'info'\n );\n }\n if (\n state.getAuthenticationHeaderOverrides() &&\n Object.entries(state.getAuthenticationHeaderOverrides()).length\n ) {\n profile.authenticationHeaderOverrides =\n state.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(state.getAuthenticationHeaderOverrides(), 'info');\n }\n\n // remove the helper key 'tenant'\n delete profile.tenant;\n\n // update profiles\n profiles[state.getHost()] = profile;\n\n // sort profiles\n const orderedProfiles = Object.keys(profiles)\n .sort()\n .reduce((obj, key) => {\n obj[key] = profiles[key];\n return obj;\n }, {});\n\n // save profiles\n saveJsonToFile(orderedProfiles, filename, false);\n verboseMessage(`Saved connection profile ${state.getHost()} in ${filename}`);\n return true;\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData: ConnectionsFileInterface = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 1) {\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n printMessage(`Deleted connection profile ${profiles[0].tenant}`);\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\n/**\n * Describe connection profile\n * @param {string} host Host URL or unique substring\n * @param {boolean} showSecrets Whether secrets should be shown in clear text or not\n */\nexport async function describeConnectionProfile(\n host: string,\n showSecrets: boolean\n) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n const present = '[present]';\n const jwk = profile.svcacctJwk;\n if (!showSecrets) {\n if (profile.password) profile.password = present;\n if (profile.logApiSecret) profile.logApiSecret = present;\n if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;\n }\n if (!profile.username) {\n delete profile.username;\n delete profile.password;\n }\n if (!profile.logApiKey) {\n delete profile.logApiKey;\n delete profile.logApiSecret;\n }\n if (!profile.svcacctId) {\n delete profile.svcacctId;\n delete profile.svcacctJwk;\n }\n if (showSecrets && jwk) {\n (profile as unknown)['svcacctJwk'] = 'see below';\n }\n if (!profile.authenticationService) {\n delete profile.authenticationService;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n logApiKey: 'Log API Key',\n logApiSecret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n svcacctId: 'Service Account Id',\n svcacctJwk: 'Service Account JWK',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n if (showSecrets && jwk) {\n printMessage(jwk, 'data');\n }\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n\n/**\n * Create a new service account using auto-generated parameters\n * @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object\n */\nexport async function addNewServiceAccount(): Promise<ObjectSkeletonInterface> {\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: start`);\n const name = `Frodo-SA-${new Date().getTime()}`;\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: name=${name}...`);\n const description = `${state.getUsername()}'s Frodo Service Account`;\n const scope = ['fr:am:*', 'fr:idm:*', 'fr:idc:esv:*'];\n const jwkPrivate = await createJwkRsa();\n const jwkPublic = await getJwkRsaPublic(jwkPrivate);\n const jwks = createJwks(jwkPublic);\n const sa = await createServiceAccount(\n name,\n description,\n 'Active',\n scope,\n jwks\n );\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: id=${sa._id}`);\n state.setServiceAccountId(sa._id);\n state.setServiceAccountJwk(jwkPrivate);\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: end`);\n return sa;\n}\n"],"mappings":"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"}
@@ -53,18 +53,24 @@ export function listConnectionProfiles(long = false) {
53
53
  try {
54
54
  const data = fs.readFileSync(filename, 'utf8');
55
55
  const connectionsData = JSON.parse(data);
56
- if (long) {
57
- const table = createTable(['Host', 'Username', 'Log API Key']);
58
- Object.keys(connectionsData).forEach(c => {
59
- table.push([c, connectionsData[c].username, connectionsData[c].logApiKey]);
60
- });
61
- printMessage(table.toString(), 'data');
56
+ if (Object.keys(connectionsData).length < 1) {
57
+ printMessage(`No connections defined yet in ${filename}`, 'info');
62
58
  } else {
63
- Object.keys(connectionsData).forEach(c => {
64
- printMessage(`${c}`, 'data');
65
- });
59
+ if (long) {
60
+ const table = createTable(['Host', 'Username', 'Log API Key']);
61
+ Object.keys(connectionsData).forEach(c => {
62
+ table.push([c, connectionsData[c].username, connectionsData[c].logApiKey]);
63
+ });
64
+ printMessage(table.toString(), 'data');
65
+ } else {
66
+ Object.keys(connectionsData).forEach(c => {
67
+ printMessage(`${c}`, 'data');
68
+ });
69
+ // getUniqueNames(5, Object.keys(connectionsData));
70
+ }
71
+
72
+ printMessage('Any unique substring of a saved host can be used as the value for host parameter in all commands', 'info');
66
73
  }
67
- printMessage('Any unique substring of a saved host can be used as the value for host parameter in all commands', 'info');
68
74
  } catch (e) {
69
75
  printMessage(`No connections found in ${filename} (${e.message})`, 'error');
70
76
  }
@@ -75,12 +81,19 @@ export function listConnectionProfiles(long = false) {
75
81
  */
76
82
  function migrateFromLegacyProfile() {
77
83
  const legacyPath = `${os.homedir()}/.frodo/${legacyProfileFilename}`;
78
- if (fs.existsSync(legacyPath)) {
79
- fs.copyFileSync(legacyPath, `${os.homedir()}/.frodo/${newProfileFilename}`);
84
+ const newPath = `${os.homedir()}/.frodo/${newProfileFilename}`;
85
+ if (!fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {
86
+ // no connections file (old or new), create empty new one
87
+ fs.writeFileSync(newPath, JSON.stringify({}, null, fileOptions.indentation));
88
+ } else if (fs.existsSync(legacyPath) && !fs.existsSync(newPath)) {
89
+ // old exists, new one does not - so copy old to new one
90
+ fs.copyFileSync(legacyPath, newPath);
80
91
  // for now, just add a "deprecated" suffix. May delete the old file
81
92
  // in a future release
82
93
  fs.renameSync(legacyPath, `${legacyPath}.deprecated`);
83
94
  }
95
+ // in other cases, where
96
+ // (both old and new exist) OR (only new one exists) don't do anything
84
97
  }
85
98
 
86
99
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rockcarver/frodo-lib",
3
- "version": "0.18.0",
3
+ "version": "0.18.1-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/ConnectionProfileOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,MAAM,EAAE,MAAM,WAAW,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU1D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,gCAAgC,CAAC;CACjD;AAKD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAuBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,UAAQ,QA2BlD;AAeD;;;;;GAKG;AACH,wBAAsB,sBAAsB,kBAiD3C;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,0BAA0B,CAAC,CAgDrC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAEhF;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA4F1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,iBAgDrB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAqB7E","file":"ConnectionProfileOps.d.ts","sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport * as state from '../shared/State';\nimport DataProtection from './utils/DataProtection';\nimport {\n createObjectTable,\n createTable,\n debugMessage,\n printMessage,\n verboseMessage,\n} from './utils/Console';\nimport { FRODO_CONNECTION_PROFILES_PATH_KEY } from '../storage/StaticStorage';\nimport { createJwkRsa, createJwks, getJwkRsaPublic, JwkRsa } from './JoseOps';\nimport { createServiceAccount } from './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 (long) {\n const table = createTable(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n }\n printMessage(\n 'Any unique substring of a saved host can be used as the value for host parameter in all commands',\n 'info'\n );\n } catch (e) {\n printMessage(`No connections found in ${filename} (${e.message})`, 'error');\n }\n}\n\n/**\n * Migrate from .frodorc to Connections.json\n */\nfunction migrateFromLegacyProfile() {\n const legacyPath = `${os.homedir()}/.frodo/${legacyProfileFilename}`;\n if (fs.existsSync(legacyPath)) {\n fs.copyFileSync(legacyPath, `${os.homedir()}/.frodo/${newProfileFilename}`);\n // for now, just add a \"deprecated\" suffix. May delete the old file\n // in a future release\n fs.renameSync(legacyPath, `${legacyPath}.deprecated`);\n }\n}\n\n/**\n * Initialize connection profiles\n *\n * This method is called from app.ts and runs before any of the message handlers are registered.\n * Therefore none of the Console message functions will produce any output.\n */\nexport async function initConnectionProfiles() {\n // create connections.json file if it doesn't exist\n const filename = getConnectionProfilesPath();\n const folderName = path.dirname(filename);\n if (!fs.existsSync(folderName)) {\n fs.mkdirSync(folderName, { recursive: true });\n if (!fs.existsSync(filename)) {\n fs.writeFileSync(\n filename,\n JSON.stringify({}, null, fileOptions.indentation)\n );\n }\n }\n // encrypt the password and logApiSecret from clear text to aes-256-GCM\n else {\n migrateFromLegacyProfile();\n const data = fs.readFileSync(filename, 'utf8');\n const connectionsData: ConnectionsFileInterface = JSON.parse(data);\n let convert = false;\n for (const conn of Object.keys(connectionsData)) {\n if (connectionsData[conn]['password']) {\n convert = true;\n connectionsData[conn].encodedPassword = await crypto.encrypt(\n connectionsData[conn]['password']\n );\n delete connectionsData[conn]['password'];\n }\n if (connectionsData[conn]['logApiSecret']) {\n convert = true;\n connectionsData[conn].encodedLogApiSecret = await crypto.encrypt(\n connectionsData[conn]['logApiSecret']\n );\n delete connectionsData[conn]['logApiSecret'];\n }\n if (connectionsData[conn]['svcacctJwk']) {\n convert = true;\n connectionsData[conn].encodedSvcacctJwk = await crypto.encrypt(\n connectionsData[conn]['svcacctJwk']\n );\n delete connectionsData[conn]['svcacctJwk'];\n }\n }\n if (convert) {\n fs.writeFileSync(\n filename,\n JSON.stringify(connectionsData, null, fileOptions.indentation)\n );\n }\n }\n}\n\n/**\n * Get connection profile by host\n * @param {String} host host tenant host url or unique substring\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfileByHost(\n host: string\n): Promise<ConnectionProfileInterface> {\n try {\n const filename = getConnectionProfilesPath();\n const connectionsData = JSON.parse(fs.readFileSync(filename, 'utf8'));\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 0) {\n printMessage(\n `Profile for ${host} not found. Please specify credentials on command line`,\n 'error'\n );\n return null;\n }\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n }\n return {\n tenant: profiles[0].tenant,\n username: profiles[0].username ? profiles[0].username : null,\n password: profiles[0].encodedPassword\n ? await crypto.decrypt(profiles[0].encodedPassword)\n : null,\n logApiKey: profiles[0].logApiKey ? profiles[0].logApiKey : null,\n logApiSecret: profiles[0].encodedLogApiSecret\n ? await crypto.decrypt(profiles[0].encodedLogApiSecret)\n : null,\n authenticationService: profiles[0].authenticationService\n ? profiles[0].authenticationService\n : null,\n authenticationHeaderOverrides: profiles[0].authenticationHeaderOverrides\n ? profiles[0].authenticationHeaderOverrides\n : {},\n svcacctId: profiles[0].svcacctId ? profiles[0].svcacctId : null,\n svcacctJwk: profiles[0].encodedSvcacctJwk\n ? await crypto.decrypt(profiles[0].encodedSvcacctJwk)\n : null,\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile(): Promise<ConnectionProfileInterface> {\n return getConnectionProfileByHost(state.getHost());\n}\n\n/**\n * Save connection profile\n * @param {string} host host url for new profiles or unique substring for existing profiles\n * @returns {Promise<boolean>} true if the operation succeeded, false otherwise\n */\nexport async function saveConnectionProfile(host: string): Promise<boolean> {\n const filename = getConnectionProfilesPath();\n verboseMessage(`Saving connection profile in ${filename}`);\n let profiles: ConnectionsFileInterface = {};\n let profile: SecureConnectionProfileInterface = { tenant: '' };\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n profiles = JSON.parse(data);\n\n // find tenant\n const found = findConnectionProfiles(profiles, host);\n\n // replace tenant in session with real tenant url if necessary\n if (found.length === 1) {\n profile = found[0];\n state.setHost(profile.tenant);\n verboseMessage(`Existing profile: ${profile.tenant}`);\n }\n\n // connection profile not found, validate host is a real URL\n if (found.length === 0) {\n if (isValidUrl(host)) {\n state.setHost(host);\n verboseMessage(`New profile: ${host}`);\n } else {\n printMessage(\n `No existing profile found matching '${host}'. Provide a valid URL as the host argument to create a new profile.`,\n 'error'\n );\n return false;\n }\n }\n } catch (error) {\n verboseMessage(`New profiles file ${filename} with new profile ${host}`);\n }\n\n // user account\n if (state.getUsername()) profile.username = state.getUsername();\n if (state.getPassword())\n profile.encodedPassword = await crypto.encrypt(state.getPassword());\n\n // log API\n if (state.getLogApiKey()) profile.logApiKey = state.getLogApiKey();\n if (state.getLogApiSecret())\n profile.encodedLogApiSecret = await crypto.encrypt(state.getLogApiSecret());\n\n // service account\n if (state.getServiceAccountId())\n profile.svcacctId = state.getServiceAccountId();\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n\n // advanced settings\n if (state.getAuthenticationService()) {\n profile.authenticationService = state.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n state.getAuthenticationService(),\n 'info'\n );\n }\n if (\n state.getAuthenticationHeaderOverrides() &&\n Object.entries(state.getAuthenticationHeaderOverrides()).length\n ) {\n profile.authenticationHeaderOverrides =\n state.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(state.getAuthenticationHeaderOverrides(), 'info');\n }\n\n // remove the helper key 'tenant'\n delete profile.tenant;\n\n // update profiles\n profiles[state.getHost()] = profile;\n\n // sort profiles\n const orderedProfiles = Object.keys(profiles)\n .sort()\n .reduce((obj, key) => {\n obj[key] = profiles[key];\n return obj;\n }, {});\n\n // save profiles\n saveJsonToFile(orderedProfiles, filename, false);\n verboseMessage(`Saved connection profile ${state.getHost()} in ${filename}`);\n return true;\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData: ConnectionsFileInterface = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 1) {\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n printMessage(`Deleted connection profile ${profiles[0].tenant}`);\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\n/**\n * Describe connection profile\n * @param {string} host Host URL or unique substring\n * @param {boolean} showSecrets Whether secrets should be shown in clear text or not\n */\nexport async function describeConnectionProfile(\n host: string,\n showSecrets: boolean\n) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n const present = '[present]';\n const jwk = profile.svcacctJwk;\n if (!showSecrets) {\n if (profile.password) profile.password = present;\n if (profile.logApiSecret) profile.logApiSecret = present;\n if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;\n }\n if (!profile.username) {\n delete profile.username;\n delete profile.password;\n }\n if (!profile.logApiKey) {\n delete profile.logApiKey;\n delete profile.logApiSecret;\n }\n if (!profile.svcacctId) {\n delete profile.svcacctId;\n delete profile.svcacctJwk;\n }\n if (showSecrets && jwk) {\n (profile as unknown)['svcacctJwk'] = 'see below';\n }\n if (!profile.authenticationService) {\n delete profile.authenticationService;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n logApiKey: 'Log API Key',\n logApiSecret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n svcacctId: 'Service Account Id',\n svcacctJwk: 'Service Account JWK',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n if (showSecrets && jwk) {\n printMessage(jwk, 'data');\n }\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n\n/**\n * Create a new service account using auto-generated parameters\n * @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object\n */\nexport async function addNewServiceAccount(): Promise<ObjectSkeletonInterface> {\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: start`);\n const name = `Frodo-SA-${new Date().getTime()}`;\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: name=${name}...`);\n const description = `${state.getUsername()}'s Frodo Service Account`;\n const scope = ['fr:am:*', 'fr:idm:*', 'fr:idc:esv:*'];\n const jwkPrivate = await createJwkRsa();\n const jwkPublic = await getJwkRsaPublic(jwkPrivate);\n const jwks = createJwks(jwkPublic);\n const sa = await createServiceAccount(\n name,\n description,\n 'Active',\n scope,\n jwks\n );\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: id=${sa._id}`);\n state.setServiceAccountId(sa._id);\n state.setServiceAccountJwk(jwkPrivate);\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: end`);\n return sa;\n}\n"]}
1
+ {"version":3,"sources":["../src/ops/ConnectionProfileOps.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,MAAM,EAAE,MAAM,WAAW,CAAC;AAE9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU1D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,6BAA6B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,gCAAgC,CAAC;CACjD;AAKD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAuBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,UAAQ,QAgClD;AAyBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,kBAiD3C;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,0BAA0B,CAAC,CAgDrC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAEhF;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA4F1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,KAAA,QAiC3C;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,OAAO,iBAgDrB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAqB7E","file":"ConnectionProfileOps.d.ts","sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport * as state from '../shared/State';\nimport DataProtection from './utils/DataProtection';\nimport {\n createObjectTable,\n createTable,\n debugMessage,\n printMessage,\n verboseMessage,\n} from './utils/Console';\nimport { FRODO_CONNECTION_PROFILES_PATH_KEY } from '../storage/StaticStorage';\nimport { createJwkRsa, createJwks, getJwkRsaPublic, JwkRsa } from './JoseOps';\nimport { createServiceAccount } from './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(['Host', 'Username', 'Log API Key']);\n Object.keys(connectionsData).forEach((c) => {\n table.push([\n c,\n connectionsData[c].username,\n connectionsData[c].logApiKey,\n ]);\n });\n printMessage(table.toString(), 'data');\n } else {\n Object.keys(connectionsData).forEach((c) => {\n printMessage(`${c}`, 'data');\n });\n // 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 svcacctId: profiles[0].svcacctId ? profiles[0].svcacctId : null,\n svcacctJwk: profiles[0].encodedSvcacctJwk\n ? await crypto.decrypt(profiles[0].encodedSvcacctJwk)\n : null,\n };\n } catch (e) {\n printMessage(\n `Can not read saved connection info, please specify credentials on command line: ${e}`,\n 'error'\n );\n return null;\n }\n}\n\n/**\n * Get connection profile\n * @returns {Object} connection profile or null\n */\nexport async function getConnectionProfile(): Promise<ConnectionProfileInterface> {\n return getConnectionProfileByHost(state.getHost());\n}\n\n/**\n * Save connection profile\n * @param {string} host host url for new profiles or unique substring for existing profiles\n * @returns {Promise<boolean>} true if the operation succeeded, false otherwise\n */\nexport async function saveConnectionProfile(host: string): Promise<boolean> {\n const filename = getConnectionProfilesPath();\n verboseMessage(`Saving connection profile in ${filename}`);\n let profiles: ConnectionsFileInterface = {};\n let profile: SecureConnectionProfileInterface = { tenant: '' };\n try {\n fs.statSync(filename);\n const data = fs.readFileSync(filename, 'utf8');\n profiles = JSON.parse(data);\n\n // find tenant\n const found = findConnectionProfiles(profiles, host);\n\n // replace tenant in session with real tenant url if necessary\n if (found.length === 1) {\n profile = found[0];\n state.setHost(profile.tenant);\n verboseMessage(`Existing profile: ${profile.tenant}`);\n }\n\n // connection profile not found, validate host is a real URL\n if (found.length === 0) {\n if (isValidUrl(host)) {\n state.setHost(host);\n verboseMessage(`New profile: ${host}`);\n } else {\n printMessage(\n `No existing profile found matching '${host}'. Provide a valid URL as the host argument to create a new profile.`,\n 'error'\n );\n return false;\n }\n }\n } catch (error) {\n verboseMessage(`New profiles file ${filename} with new profile ${host}`);\n }\n\n // user account\n if (state.getUsername()) profile.username = state.getUsername();\n if (state.getPassword())\n profile.encodedPassword = await crypto.encrypt(state.getPassword());\n\n // log API\n if (state.getLogApiKey()) profile.logApiKey = state.getLogApiKey();\n if (state.getLogApiSecret())\n profile.encodedLogApiSecret = await crypto.encrypt(state.getLogApiSecret());\n\n // service account\n if (state.getServiceAccountId())\n profile.svcacctId = state.getServiceAccountId();\n if (state.getServiceAccountJwk())\n profile.encodedSvcacctJwk = await crypto.encrypt(\n state.getServiceAccountJwk()\n );\n\n // advanced settings\n if (state.getAuthenticationService()) {\n profile.authenticationService = state.getAuthenticationService();\n printMessage(\n 'Advanced setting: Authentication Service: ' +\n state.getAuthenticationService(),\n 'info'\n );\n }\n if (\n state.getAuthenticationHeaderOverrides() &&\n Object.entries(state.getAuthenticationHeaderOverrides()).length\n ) {\n profile.authenticationHeaderOverrides =\n state.getAuthenticationHeaderOverrides();\n printMessage('Advanced setting: Authentication Header Overrides: ', 'info');\n printMessage(state.getAuthenticationHeaderOverrides(), 'info');\n }\n\n // remove the helper key 'tenant'\n delete profile.tenant;\n\n // update profiles\n profiles[state.getHost()] = profile;\n\n // sort profiles\n const orderedProfiles = Object.keys(profiles)\n .sort()\n .reduce((obj, key) => {\n obj[key] = profiles[key];\n return obj;\n }, {});\n\n // save profiles\n saveJsonToFile(orderedProfiles, filename, false);\n verboseMessage(`Saved connection profile ${state.getHost()} in ${filename}`);\n return true;\n}\n\n/**\n * Delete connection profile\n * @param {String} host host tenant host url or unique substring\n */\nexport function deleteConnectionProfile(host) {\n const filename = getConnectionProfilesPath();\n let connectionsData: ConnectionsFileInterface = {};\n fs.stat(filename, (err) => {\n if (err == null) {\n const data = fs.readFileSync(filename, 'utf8');\n connectionsData = JSON.parse(data);\n const profiles = findConnectionProfiles(connectionsData, host);\n if (profiles.length == 1) {\n delete connectionsData[profiles[0].tenant];\n fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2));\n printMessage(`Deleted connection profile ${profiles[0].tenant}`);\n } else {\n if (profiles.length > 1) {\n printMessage(`Multiple matching profiles found.`, 'error');\n profiles.forEach((p) => {\n printMessage(`- ${p.tenant}`, 'error');\n });\n printMessage(`Please specify a unique sub-string`, 'error');\n return null;\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n }\n } else if (err.code === 'ENOENT') {\n printMessage(`Connection profile file ${filename} not found`);\n } else {\n printMessage(\n `Error in deleting connection profile: ${err.code}`,\n 'error'\n );\n }\n });\n}\n\n/**\n * Describe connection profile\n * @param {string} host Host URL or unique substring\n * @param {boolean} showSecrets Whether secrets should be shown in clear text or not\n */\nexport async function describeConnectionProfile(\n host: string,\n showSecrets: boolean\n) {\n const profile = await getConnectionProfileByHost(host);\n if (profile) {\n const present = '[present]';\n const jwk = profile.svcacctJwk;\n if (!showSecrets) {\n if (profile.password) profile.password = present;\n if (profile.logApiSecret) profile.logApiSecret = present;\n if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;\n }\n if (!profile.username) {\n delete profile.username;\n delete profile.password;\n }\n if (!profile.logApiKey) {\n delete profile.logApiKey;\n delete profile.logApiSecret;\n }\n if (!profile.svcacctId) {\n delete profile.svcacctId;\n delete profile.svcacctJwk;\n }\n if (showSecrets && jwk) {\n (profile as unknown)['svcacctJwk'] = 'see below';\n }\n if (!profile.authenticationService) {\n delete profile.authenticationService;\n }\n const keyMap = {\n tenant: 'Host',\n username: 'Username',\n password: 'Password',\n logApiKey: 'Log API Key',\n logApiSecret: 'Log API Secret',\n authenticationService: 'Authentication Service',\n authenticationHeaderOverrides: 'Authentication Header Overrides',\n svcacctId: 'Service Account Id',\n svcacctJwk: 'Service Account JWK',\n };\n const table = createObjectTable(profile, keyMap);\n printMessage(table.toString(), 'data');\n if (showSecrets && jwk) {\n printMessage(jwk, 'data');\n }\n } else {\n printMessage(`No connection profile ${host} found`);\n }\n}\n\n/**\n * Create a new service account using auto-generated parameters\n * @returns {Promise<ObjectSkeletonInterface>} A promise resolving to a service account object\n */\nexport async function addNewServiceAccount(): Promise<ObjectSkeletonInterface> {\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: start`);\n const name = `Frodo-SA-${new Date().getTime()}`;\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: name=${name}...`);\n const description = `${state.getUsername()}'s Frodo Service Account`;\n const scope = ['fr:am:*', 'fr:idm:*', 'fr:idc:esv:*'];\n const jwkPrivate = await createJwkRsa();\n const jwkPublic = await getJwkRsaPublic(jwkPrivate);\n const jwks = createJwks(jwkPublic);\n const sa = await createServiceAccount(\n name,\n description,\n 'Active',\n scope,\n jwks\n );\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: id=${sa._id}`);\n state.setServiceAccountId(sa._id);\n state.setServiceAccountJwk(jwkPrivate);\n debugMessage(`ConnectionProfileOps.addNewServiceAccount: end`);\n return sa;\n}\n"]}