Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"request": "launch",
"name": "Run Frodo CLI",
"program": "${workspaceFolder}/src/app.ts",
"args": ["config-manager", "push", "terms-and-conditions", "dev"],
"args": ["config-manager", "push", "password-policy", "dev"],
"outFiles": [
"${workspaceFolder}/dist/**/*.(m|c|)js",
"${workspaceFolder}/node_modules/@rockcarver/frodo-lib/dist/**/*.(m|c|)js"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Option } from 'commander';

import { configManagerImportPasswordPolicy } from '../../../configManagerOps/FrConfigPasswordPolicyOps';
import { getTokens } from '../../../ops/AuthenticateOps';
import { printMessage, verboseMessage } from '../../../utils/Console';
import { FrodoCommand } from '../../FrodoCommand';

const deploymentTypes = ['cloud', 'forgeops'];

export default function setup() {
const program = new FrodoCommand(
'frodo config-manager push password-policy',
[],
deploymentTypes
);

program
.description('Import password-policy objects.')
.addOption(
new Option(
'-r, --realm <realm>',
'Specifies the realm to Import from. Only the entity object from this realm will be imported.'
)
)
.action(async (host, realm, user, password, options, command) => {
command.handleDefaultArgsAndOpts(
host,
realm,
user,
password,
options,
command
);
if (options.realm) {
realm = options.realm;
}
if (await getTokens(false, true, deploymentTypes)) {
verboseMessage('Importing config entity password-policy');
const outcome = await configManagerImportPasswordPolicy(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
Expand Up @@ -33,7 +33,7 @@ export default function setup() {
);

if (await getTokens(false, true, deploymentTypes)) {
verboseMessage('Exporting config entity ui-configuration');
verboseMessage('Importing config entity ui-configuration');
const outcome = await configManagerImportUiConfig(options.file);
if (!outcome) process.exitCode = 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FrodoStubCommand } from '../../FrodoCommand';
import TermsAndConditions from './config-manager-push-terms-and-conditions';
import Themes from './config-manager-push-themes';
import UiConfig from './config-manager-push-uiConfig';
import PasswordPolicy from './config-manager-push-password-policy'

export default function setup() {
const program = new FrodoStubCommand('push').description(
Expand All @@ -11,6 +12,7 @@ export default function setup() {
program.addCommand(Themes().name('themes'));
program.addCommand(UiConfig().name('ui-config'));
program.addCommand(TermsAndConditions().name('terms-and-conditions'));
program.addCommand(PasswordPolicy().name('password-policy'))

return program;
}
41 changes: 40 additions & 1 deletion src/configManagerOps/FrConfigPasswordPolicyOps.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { frodo } from '@rockcarver/frodo-lib';
import fs from 'fs';

import { getIdmImportExportOptions } from '../ops/IdmOps';
import { printError } from '../utils/Console';
import { realmList } from '../utils/FrConfig';

const { getFilePath, saveJsonToFile } = frodo.utils;
const { exportConfigEntity } = frodo.idm.config;
const { exportConfigEntity, importConfigEntities } = frodo.idm.config;

/**
* Export an IDM configuration object in the fr-config-manager format.
Expand All @@ -29,6 +30,8 @@ export async function configManagerExportPasswordPolicy(
saveJsonToFile(realmData, getFilePath(fileName, true), false, true);
} else {
for (const realmName of await realmList()) {
// bypassing root realm
if (realmName === '/') continue;
const realmData = (
await exportConfigEntity(`fieldPolicy/${realmName}_user`, {
envReplaceParams: options.envReplaceParams,
Expand All @@ -46,3 +49,39 @@ export async function configManagerExportPasswordPolicy(
}
return false;
}

export async function configManagerImportPasswordPolicy( realm?: string, ): Promise<boolean> {
try {

if (realm && realm !== '__default__realm__') {

const filePath = getFilePath(`realms/${realm}/password-policy/${realm}_user-password-policy.json`);
const mainFile = fs.readFileSync(filePath, 'utf8')
let importData = JSON.parse(mainFile)
const id = importData._id
importData = { idm: { [id]: importData } };


await importConfigEntities(importData)
// saveJsonToFile(fileContent, 'indvidual-password-export')
}

else {
for (const realmName of await realmList()) {
if (realmName === '/') continue;
const filePath = getFilePath(`realms/${realmName}/password-policy/${realmName}_user-password-policy.json`);
const mainFile = fs.readFileSync(filePath, 'utf8')
let importData = JSON.parse(mainFile)
const id = importData._id
importData = { idm: { [id]: importData } };
await importConfigEntities(importData)
// saveJsonToFile(fileContent, 'password-export')
} }

return true;
} catch (error) {
printError(error, `Error importing config entity ui-configuration`);
}
return false;
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ Options:
Commands:
help display help for command
pull Export cloud configuration using fr-config-manager.
push Export cloud configuration using fr-config-manager.
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`"frodo config-manager push password-policy -D test/e2e/exports/fr-config-manager -m forgeops": should import the password policy into forgeops" 1`] = `
"Usage: frodo config-manager push password-policy [options] [host] [realm] [username] [password]

Import password-policy objects.

Arguments:
host AM base URL, e.g.:
https://cdk.iam.example.com/am. To use a
connection profile, just specify a
unique substring.
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")
--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> Specifies the realm to Import from. Only
the entity object from this realm will
be imported.
--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.
--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_DEBUG: Set to any value to enable debug output. Same as '--debug'.
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,72 @@
/**
* 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
FRODO_MOCK=record FRODO_NO_CACHE=1 FRODO_HOST=https://nightly.gcp.forgeops.com/am frodo config-manager push password-policy -D test/e2e/exports/fr-config-manager -m forgeops
*/


import cp from 'child_process';
import { promisify } from 'util';
import { getEnv, removeAnsiEscapeCodes } from '../utils/TestUtils';
import { forgeops_connection as fc } from '../utils/TestConfig';


const exec = promisify(cp.exec);

process.env['FRODO_MOCK'] = '1';
const forgeopsEnv = getEnv(fc);

const allDirectory = "test/e2e/exports/fr-config-manager";

test(`"frodo config-manager push password-policy -D ${allDirectory} -m forgeops": should import the password policy into forgeops"`, async () => {
const CMD = `frodo config-manager push password-policy -D ${allDirectory} -m forgeops`;
const { stdout } = await exec(CMD, forgeopsEnv);
expect(removeAnsiEscapeCodes(stdout)).toMatchSnapshot();
});