-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Add config-manager push secret-mappings command #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { frodo } from '@rockcarver/frodo-lib'; | ||
| import { Option } from 'commander'; | ||
|
|
||
| import { configManagerImportSecretMappings } from '../../../configManagerOps/FrConfigSecretMappingsOps'; | ||
| import { getTokens } from '../../../ops/AuthenticateOps'; | ||
| import { printMessage, verboseMessage } from '../../../utils/Console'; | ||
| import { FrodoCommand } from '../../FrodoCommand'; | ||
|
|
||
| const { CLOUD_DEPLOYMENT_TYPE_KEY } = frodo.utils.constants; | ||
|
|
||
| const deploymentTypes = [CLOUD_DEPLOYMENT_TYPE_KEY]; | ||
|
|
||
| export default function setup() { | ||
| const program = new FrodoCommand( | ||
| 'frodo config-manager push secret-mappings', | ||
| [], | ||
| deploymentTypes | ||
| ); | ||
|
|
||
| program | ||
| .description('Import secret mappings.') | ||
| .addOption( | ||
| new Option( | ||
| '-n, --name <name>', | ||
| 'Name of the secret mapping, It will only import secret mapping with the name. Works both with mapping._id or alias.' | ||
| ) | ||
| ) | ||
| .addOption( | ||
| new Option( | ||
| '-r, --realm <realm>', | ||
| 'Specific realm to get secret mappings from (overrides environment)' | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
| ) | ||
| .action(async (host, realm, user, password, options, command) => { | ||
| command.handleDefaultArgsAndOpts( | ||
| host, | ||
| realm, | ||
| user, | ||
| password, | ||
| options, | ||
| command | ||
| ); | ||
| if (options.realm) { | ||
| realm = options.realm; | ||
| } | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (await getTokens(false, true, deploymentTypes)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are going to want to check that when the |
||
| verboseMessage('Importing secret mappings...'); | ||
| const outcome = await configManagerImportSecretMappings( | ||
| options.name, | ||
| realm | ||
| ); | ||
| if (!outcome) process.exitCode = 1; | ||
| } | ||
| // unrecognized combination of options or no options | ||
| else { | ||
| printMessage( | ||
| 'Unrecognized combination of options or no options...', | ||
| 'error' | ||
| ); | ||
| program.help(); | ||
| process.exitCode = 1; | ||
| } | ||
| }); | ||
|
|
||
| return program; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,11 @@ | ||
| import { frodo, state } from '@rockcarver/frodo-lib'; | ||
| import fs from 'fs'; | ||
|
|
||
| import { printError } from '../utils/Console'; | ||
| import { realmList } from '../utils/FrConfig'; | ||
|
|
||
| const { saveJsonToFile, getFilePath } = frodo.utils; | ||
| const { readSecretStoreMappings } = frodo.secretStore; | ||
| const { readSecretStoreMappings, importSecretStores } = frodo.secretStore; | ||
|
|
||
| export async function configManagerExportSecretMappings( | ||
| name?, | ||
|
|
@@ -20,6 +21,8 @@ export async function configManagerExportSecretMappings( | |
| processSecretMappings(readData, `realms/${realm}/secret-mappings`, name); | ||
| } else { | ||
| for (const realm of await realmList()) { | ||
| // fr-config-manager doesn't support root secret-mappings | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (realm === '/') continue; | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| state.setRealm(realm); | ||
| const readData = await readSecretStoreMappings( | ||
| 'ESV', | ||
|
|
@@ -65,3 +68,40 @@ async function aliasSearch(object, name) { | |
| return false; | ||
| } | ||
| } | ||
|
|
||
| export async function configManagerImportSecretMappings( | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| name?: string, | ||
| realm?: string | ||
| ): Promise<boolean> { | ||
| try { | ||
| if (realm && realm !== '__default__realm__' && name) { | ||
| const filePath = getFilePath( | ||
| `realms/${realm}/secret-mappings/${name}.json` | ||
| ); | ||
| const readFile = fs.readFileSync(filePath, 'utf8'); | ||
| const importData = JSON.parse(readFile); | ||
| const mappingData = { secretstore: { [importData._id]: importData } }; | ||
| await importSecretStores(mappingData, false, importData._id); | ||
| } else { | ||
| for (const realmName of await realmList()) { | ||
| // fr-config-manager doesn't support root secret-mappingsgit | ||
| if (realmName === '/') continue; | ||
dallinjsevy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const filePath = getFilePath(`realms/${realmName}/secret-mappings/`); | ||
| if (!fs.existsSync(filePath)) continue; | ||
| const readDir = fs.readdirSync(filePath, 'utf8'); | ||
| for (const fileName of readDir) { | ||
| const fullPath = `${filePath}/${fileName}`; | ||
| const readFile = fs.readFileSync(fullPath, 'utf8'); | ||
| const importData = JSON.parse(readFile); | ||
| const mappingData = { secretstore: { [importData._id]: importData } }; | ||
|
|
||
| await importSecretStores(mappingData, false, importData._id); | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } catch (error) { | ||
| printError(error, `Error importing secret mappings`); | ||
| return false; | ||
| } | ||
|
Comment on lines
+76
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have bugs in this function that prevent the secret mappings from being imported, using either the -n flag or not using it. You will want to fix those |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
|
||
| exports[`CLI help interface for 'config-manager push secret-mappings' should be expected english 1`] = ` | ||
| "Usage: frodo config-manager push secret-mappings [options] [host] [realm] [username] [password] | ||
|
|
||
| Import secret mappings. | ||
|
|
||
| Arguments: | ||
| host AM base URL, e.g.: https://cdk.iam.example.com/am. To use a connection profile, just specify a unique substring or alias. | ||
| realm Realm. Specify realm as '/' for the root realm or 'realm' or '/parent/child' otherwise. (default: "alpha" for Identity Cloud tenants, "/" otherwise.) | ||
| username Username to login with. Must be an admin user with appropriate rights to manage authentication journeys/trees. | ||
| password Password. | ||
|
|
||
| Options: | ||
| --curlirize Output all network calls in curl format. | ||
| -D, --directory <directory> Set the working directory. | ||
| --debug Debug output during command execution. If specified, may or may not produce additional output helpful for troubleshooting. | ||
| --flush-cache Flush token cache. | ||
| -h, --help Help | ||
| --idm-host <idm-host> IDM base URL, e.g.: https://cdk.idm.example.com/myidm. Use only if your IDM installation resides in a different domain and/or if the base path differs from the default "/openidm". | ||
| -k, --insecure Allow insecure connections when using SSL/TLS. Has no effect when using a network proxy for https (HTTPS_PROXY=http://<host>:<port>), in that case the proxy must provide this capability. (default: Don't allow insecure connections) | ||
| --login-client-id <client-id> Specify a custom OAuth2 client id to use a your own oauth2 client for IDM API calls in deployments of type "cloud" or "forgeops". Your custom client must be configured as a public client and allow the authorization code grant using the "openid fr:idm:*" scope. Use the "--redirect-uri" parameter if you have configured a custom redirect uri (default: "<host>/platform/appAuthHelperRedirect.html"). | ||
| --login-redirect-uri <redirect-uri> Specify a custom redirect URI to use with your custom OAuth2 client (efault: "<host>/platform/appAuthHelperRedirect.html"). | ||
| -m, --type <type> Override auto-detected deployment type. Valid values for type: | ||
| classic: A classic Access Management-only deployment with custom layout and configuration. | ||
| cloud: A ForgeRock Identity Cloud environment. | ||
| forgeops: A ForgeOps CDK or CDM deployment. | ||
| The detected or provided deployment type controls certain behavior like obtaining an Identity Management admin token or not and whether to export/import referenced email templates or how to walk through the tenant admin login flow of Identity Cloud and handle MFA (choices: "classic", "cloud", "forgeops") | ||
| -n, --name <name> Name of the secret mapping, It will only import secret mapping with the name. Works both with mapping._id or alias. | ||
| --no-cache Disable token cache for this operation. | ||
| --passphrase <passphrase> The passphrase for the Amster private key if it is encrypted. | ||
| --private-key <file> File containing the private key for authenticating with Amster. Supported formats include PEM (both PKCS#1 and PKCS#8 variants), OpenSSH, DNSSEC, and JWK. | ||
| -r, --realm <realm> Specific realm to get secret mappings from (overrides environment) | ||
| --retry <strategy> Retry failed operations. Valid values for strategy: | ||
| everything: Retry all failed operations. | ||
| network: Retry only network-related failed operations. | ||
| nothing: Do not retry failed operations. | ||
| The selected retry strategy controls how the CLI handles failures. (choices: "nothing", "everything", "network", default: Do not retry failed operations.) | ||
| --sa-id <sa-id> Service account id. | ||
| --sa-jwk-file <file> File containing the JSON Web Key (JWK) associated with the the service account. | ||
| --use-realm-prefix-on-managed-objects Set to true if you want to use the realm name as a prefix on managed object configuration, e.g. managed/alpha_user, managed/alpha_application or managed/bravo_organization. When false, the default behaviour of using managed/user etc. is retained. This option is ignored when the deployment type is "cloud". | ||
| --verbose Verbose output during command execution. If specified, may or may not produce additional output. | ||
|
|
||
| Environment Variables: | ||
| FRODO_HOST: AM base URL. Overridden by 'host' argument. | ||
| FRODO_IDM_HOST: IDM base URL. Overridden by '--idm-host' option. | ||
| FRODO_REALM: Realm. Overridden by 'realm' argument. | ||
| FRODO_USERNAME: Username. Overridden by 'username' argument. | ||
| FRODO_PASSWORD: Password. Overridden by 'password' argument. | ||
| FRODO_LOGIN_CLIENT_ID: OAuth2 client id for IDM API calls. Overridden by '--login-client-id' option. | ||
| FRODO_LOGIN_REDIRECT_URI: Redirect Uri for custom OAuth2 client id. Overridden by '--login-redirect-uri' option. | ||
| FRODO_SA_ID: Service account uuid. Overridden by '--sa-id' option. | ||
| FRODO_SA_JWK: Service account JWK. Overridden by '--sa-jwk-file' option but takes the actual JWK as a value, not a file name. | ||
| FRODO_AMSTER_PASSPHRASE: Passphrase for the Amster private key if it is encrypted. Overridden by '--passphrase' option. | ||
| FRODO_AMSTER_PRIVATE_KEY: Amster private key. Overridden by '--private-key' option but takes the actual private key as a value (i.e. the file contents), not a file name. Supported formats include PEM (both PKCS#1 and PKCS#8 variants), OpenSSH, DNSSEC, and JWK. | ||
| FRODO_NO_CACHE: Disable token cache. Same as '--no-cache' option. | ||
| FRODO_TOKEN_CACHE_PATH: Use this token cache file instead of '~/.frodo/TokenCache.json'. | ||
| FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'. | ||
| FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use. When using an Amster private key, specifies which journey to use for Amster authentication as opposed to the default 'amsterService' journey. | ||
| FRODO_AUTHENTICATION_HEADER_OVERRIDES: Map of headers: '{"host":"am.example.com:8081"}'. These headers are sent with all requests and can be used to override default behavior, for example to set a custom host header for Proxy Connect-protected PingOne Advanced Identity Cloud environments. | ||
| FRODO_CONFIGURATION_HEADER_OVERRIDES: Map of headers: '{"X-Configuration-Type":"mutable"}'. These headers are sent with all configuration requests and can be used to override default behavior, for example to set a custom configuration header for mutable PingOne Advanced Identity Cloud environments. | ||
| FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'. | ||
| FRODO_IGA: Set to "true" to enable IGA (Identity Governance) endpoints for cloud deployments, or "false" to disable them, overriding auto-detected value. | ||
| FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file. | ||
| FRODO_MASTER_KEY: Use this master key instead of what's in '~/.frodo/masterkey.key'. Takes precedence over FRODO_MASTER_KEY_PATH. | ||
|
|
||
| " | ||
| `; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import cp from 'child_process'; | ||
| import { promisify } from 'util'; | ||
|
|
||
| const exec = promisify(cp.exec); | ||
| const CMD = 'frodo config-manager push secret-mappings --help'; | ||
| const { stdout } = await exec(CMD); | ||
|
|
||
| test("CLI help interface for 'config-manager push secret-mappings' should be expected english", async () => { | ||
| expect(stdout).toMatchSnapshot(); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
|
||
| exports[`frodo config-manager push secret-mappings "frodo config-manager push secret-mappings -D test/e2e/exports/fr-config-manager/cloud -m cloud": should import the password policy into cloud" 1`] = `""`; | ||
|
|
||
| exports[`frodo config-manager push secret-mappings "frodo config-manager push secret-mappings -n am.services.oauth2.stateless.signing.HMAC test/e2e/exports/fr-config-manager/cloud -m cloud": should import a specific password policy by name into cloud" 1`] = `""`; | ||
|
|
||
| exports[`frodo config-manager push secret-mappings "frodo config-manager push secret-mappings -r alpha test/e2e/exports/fr-config-manager/cloud -m cloud": should import a specific password policy by name into cloud" 1`] = `""`; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /** | ||
| * Follow this process to write e2e tests for the CLI project: | ||
| * | ||
| * 1. Test if all the necessary mocks for your tests already exist. | ||
| * In mock mode, run the command you want to test with the same arguments | ||
| * and parameters exactly as you want to test it, for example: | ||
| * | ||
| * $ FRODO_MOCK=1 frodo conn save https://openam-frodo-dev.forgeblocks.com/am volker.scheuber@forgerock.com Sup3rS3cr3t! | ||
| * | ||
| * If your command completes without errors and with the expected results, | ||
| * all the required mocks already exist and you are good to write your | ||
| * test and skip to step #4. | ||
| * | ||
| * If, however, your command fails and you see errors like the one below, | ||
| * you know you need to record the mock responses first: | ||
| * | ||
| * [Polly] [adapter:node-http] Recording for the following request is not found and `recordIfMissing` is `false`. | ||
| * | ||
| * 2. Record mock responses for your exact command. | ||
| * In mock record mode, run the command you want to test with the same arguments | ||
| * and parameters exactly as you want to test it, for example: | ||
| * | ||
| * $ FRODO_MOCK=record frodo conn save https://openam-frodo-dev.forgeblocks.com/am volker.scheuber@forgerock.com Sup3rS3cr3t! | ||
| * | ||
| * Wait until you see all the Polly instances (mock recording adapters) have | ||
| * shutdown before you try to run step #1 again. | ||
| * Messages like these indicate mock recording adapters shutting down: | ||
| * | ||
| * Polly instance 'conn/4' stopping in 3s... | ||
| * Polly instance 'conn/4' stopping in 2s... | ||
| * Polly instance 'conn/save/3' stopping in 3s... | ||
| * Polly instance 'conn/4' stopping in 1s... | ||
| * Polly instance 'conn/save/3' stopping in 2s... | ||
| * Polly instance 'conn/4' stopped. | ||
| * Polly instance 'conn/save/3' stopping in 1s... | ||
| * Polly instance 'conn/save/3' stopped. | ||
| * | ||
| * 3. Validate your freshly recorded mock responses are complete and working. | ||
| * Re-run the exact command you want to test in mock mode (see step #1). | ||
| * | ||
| * 4. Write your test. | ||
| * Make sure to use the exact command including number of arguments and params. | ||
| * | ||
| * 5. Commit both your test and your new recordings to the repository. | ||
| * Your tests are likely going to reside outside the frodo-lib project but | ||
| * the recordings must be committed to the frodo-lib project. | ||
| */ | ||
|
|
||
| /* | ||
| // ForgeOps | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should say Cloud |
||
| FRODO_MOCK=record FRODO_NO_CACHE=1 FRODO_HOST=https://nightly.gcp.forgeops.com/am frodo config-manager push secret-mappings -D test/e2e/exports/fr-config-manager/forgeops -m cloud | ||
| FRODO_MOCK=record FRODO_NO_CACHE=1 FRODO_HOST=https://nightly.gcp.forgeops.com/am frodo config-manager push secret-mappings -r alpha -D test/e2e/exports/fr-config-manager/forgeops -m cloud | ||
| FRODO_MOCK=record FRODO_NO_CACHE=1 FRODO_HOST=https://nightly.gcp.forgeops.com/am frodo config-manager push secret-mappings -n am.services.oauth2.stateless.signing.HMAC -D test/e2e/exports/fr-config-manager/forgeops -m cloud | ||
|
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FRODO_HOST is wrong, it should be the host of the frodo dev tenant (look at the other frodo tests to see what that looks like). You also don't need to have the -m flag, that's optional here if you want to remove it. |
||
|
|
||
| */ | ||
|
|
||
| import cp from 'child_process'; | ||
| import { promisify } from 'util'; | ||
| import { getEnv, removeAnsiEscapeCodes } from './utils/TestUtils'; | ||
| import { connection as c } from './utils/TestConfig'; | ||
|
|
||
| const exec = promisify(cp.exec); | ||
|
|
||
| process.env['FRODO_MOCK'] = '1'; | ||
| const cloudEnv = getEnv(c); | ||
|
|
||
| const allDirectory = "test/e2e/exports/fr-config-manager/cloud"; | ||
|
|
||
| describe('frodo config-manager push secret-mappings', () => { | ||
| test(`"frodo config-manager push secret-mappings -D ${allDirectory} -m cloud": should import the password policy into cloud"`, async () => { | ||
| const CMD = `frodo config-manager push secret-mappings -D ${allDirectory} -m cloud`; | ||
| const { stdout } = await exec(CMD, cloudEnv); | ||
| expect(removeAnsiEscapeCodes(stdout)).toMatchSnapshot(); | ||
| }); | ||
|
|
||
| test(`"frodo config-manager push secret-mappings -r alpha ${allDirectory} -m cloud": should import a specific password policy by name into cloud"`, async () => { | ||
| const CMD = `frodo config-manager push secret-mappings -r alpha -D ${allDirectory} -m cloud`; | ||
| const { stdout } = await exec(CMD, cloudEnv); | ||
| expect(removeAnsiEscapeCodes(stdout)).toMatchSnapshot(); | ||
| }); | ||
| test(`"frodo config-manager push secret-mappings -n am.services.oauth2.stateless.signing.HMAC ${allDirectory} -m cloud": should import a specific password policy by name into cloud"`, async () => { | ||
| const CMD = `frodo config-manager push secret-mappings -n am.services.oauth2.stateless.signing.HMAC -D ${allDirectory} -m cloud`; | ||
| const { stdout } = await exec(CMD, cloudEnv); | ||
| expect(removeAnsiEscapeCodes(stdout)).toMatchSnapshot(); | ||
| }); | ||
| }); | ||
|
Comment on lines
+69
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The descriptions on these tests say password policy, so they need to be updated |
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add another 3 test mappings. One more for alpha realm, and two for bravo realm |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "_id": "am.services.oauth2.stateless.signing.HMAC", | ||
| "_rev": "2072507862", | ||
| "_type": { | ||
| "_id": "mappings", | ||
| "collection": true, | ||
| "name": "Mappings" | ||
| }, | ||
| "aliases": [ | ||
| "esv-oauth2-signing-key." | ||
| ], | ||
| "secretId": "am.services.oauth2.stateless.signing.HMAC" | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.