@rockcarver/frodo-lib 0.11.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/.eslintrc +32 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/README.md +121 -0
- package/.github/workflows/pipeline.yml +287 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +512 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/docs/CONTRIBUTE.md +96 -0
- package/docs/PIPELINE.md +169 -0
- package/docs/images/npm_versioning_guidelines.png +0 -0
- package/docs/images/release_pipeline.png +0 -0
- package/jsconfig.json +6 -0
- package/package.json +95 -0
- package/resources/sampleEntitiesFile.json +8 -0
- package/resources/sampleEnvFile.env +2 -0
- package/src/api/AuthenticateApi.js +33 -0
- package/src/api/BaseApi.js +242 -0
- package/src/api/CirclesOfTrustApi.js +87 -0
- package/src/api/EmailTemplateApi.js +37 -0
- package/src/api/IdmConfigApi.js +88 -0
- package/src/api/LogApi.js +45 -0
- package/src/api/ManagedObjectApi.js +62 -0
- package/src/api/OAuth2ClientApi.js +69 -0
- package/src/api/OAuth2OIDCApi.js +73 -0
- package/src/api/OAuth2ProviderApi.js +32 -0
- package/src/api/RealmApi.js +99 -0
- package/src/api/Saml2Api.js +176 -0
- package/src/api/ScriptApi.js +84 -0
- package/src/api/SecretsApi.js +151 -0
- package/src/api/ServerInfoApi.js +41 -0
- package/src/api/SocialIdentityProvidersApi.js +114 -0
- package/src/api/StartupApi.js +45 -0
- package/src/api/ThemeApi.js +181 -0
- package/src/api/TreeApi.js +207 -0
- package/src/api/VariablesApi.js +104 -0
- package/src/api/utils/ApiUtils.js +77 -0
- package/src/api/utils/ApiUtils.test.js +96 -0
- package/src/api/utils/Base64.js +62 -0
- package/src/index.js +32 -0
- package/src/index.test.js +13 -0
- package/src/ops/AdminOps.js +901 -0
- package/src/ops/AuthenticateOps.js +342 -0
- package/src/ops/CirclesOfTrustOps.js +350 -0
- package/src/ops/ConnectionProfileOps.js +254 -0
- package/src/ops/EmailTemplateOps.js +326 -0
- package/src/ops/IdmOps.js +227 -0
- package/src/ops/IdpOps.js +342 -0
- package/src/ops/JourneyOps.js +2026 -0
- package/src/ops/LogOps.js +357 -0
- package/src/ops/ManagedObjectOps.js +34 -0
- package/src/ops/OAuth2ClientOps.js +151 -0
- package/src/ops/OrganizationOps.js +85 -0
- package/src/ops/RealmOps.js +139 -0
- package/src/ops/SamlOps.js +541 -0
- package/src/ops/ScriptOps.js +211 -0
- package/src/ops/SecretsOps.js +288 -0
- package/src/ops/StartupOps.js +114 -0
- package/src/ops/ThemeOps.js +379 -0
- package/src/ops/VariablesOps.js +185 -0
- package/src/ops/templates/OAuth2ClientTemplate.json +270 -0
- package/src/ops/templates/OrgModelUserAttributesTemplate.json +149 -0
- package/src/ops/templates/cloud/GenericExtensionAttributesTemplate.json +392 -0
- package/src/ops/templates/cloud/managed.json +4119 -0
- package/src/ops/utils/Console.js +434 -0
- package/src/ops/utils/DataProtection.js +92 -0
- package/src/ops/utils/DataProtection.test.js +28 -0
- package/src/ops/utils/ExportImportUtils.js +146 -0
- package/src/ops/utils/ExportImportUtils.test.js +119 -0
- package/src/ops/utils/OpsUtils.js +76 -0
- package/src/ops/utils/Wordwrap.js +11 -0
- package/src/storage/SessionStorage.js +45 -0
- package/src/storage/StaticStorage.js +15 -0
- package/test/e2e/journey/baseline/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/baseline/Login.journey.json +205 -0
- package/test/e2e/journey/baseline/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/baseline/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/baseline/Registration.journey.json +249 -0
- package/test/e2e/journey/baseline/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/baseline/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/baseline/allAlphaJourneys.journeys.json +1520 -0
- package/test/e2e/journey/delete/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/delete/Login.journey.json +205 -0
- package/test/e2e/journey/delete/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/delete/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/delete/Registration.journey.json +249 -0
- package/test/e2e/journey/delete/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/delete/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/delete/deleteMe.journey.json +230 -0
- package/test/e2e/journey/list/Disabled.journey.json +43 -0
- package/test/e2e/journey/list/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/list/Login.journey.json +205 -0
- package/test/e2e/journey/list/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/list/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/list/Registration.journey.json +249 -0
- package/test/e2e/journey/list/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/list/UpdatePassword.journey.json +323 -0
- package/test/e2e/setup.js +107 -0
- package/test/e2e/theme/baseline/Contrast.theme.json +95 -0
- package/test/e2e/theme/baseline/Highlander.theme.json +95 -0
- package/test/e2e/theme/baseline/Robroy.theme.json +95 -0
- package/test/e2e/theme/baseline/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/baseline/Zardoz.theme.json +95 -0
- package/test/e2e/theme/import/Contrast.theme.json +95 -0
- package/test/e2e/theme/import/Highlander.theme.json +95 -0
- package/test/e2e/theme/import/Robroy.theme.json +95 -0
- package/test/e2e/theme/import/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/import/Zardoz.default.theme.json +95 -0
- package/test/fs_tmp/.gitkeep +2 -0
- package/test/global/setup.js +65 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { applyNameCollisionPolicy } from './utils/OpsUtils.js';
|
|
4
|
+
import {
|
|
5
|
+
createProgressBar,
|
|
6
|
+
createTable,
|
|
7
|
+
printMessage,
|
|
8
|
+
stopProgressBar,
|
|
9
|
+
updateProgressBar,
|
|
10
|
+
} from './utils/Console.js';
|
|
11
|
+
import { getScriptByName, getScripts, putScript } from '../api/ScriptApi.js';
|
|
12
|
+
import wordwrap from './utils/Wordwrap.js';
|
|
13
|
+
import {
|
|
14
|
+
convertBase64TextToArray,
|
|
15
|
+
convertTextArrayToBase64,
|
|
16
|
+
getTypedFilename,
|
|
17
|
+
saveToFile,
|
|
18
|
+
titleCase,
|
|
19
|
+
validateImport,
|
|
20
|
+
} from './utils/ExportImportUtils.js';
|
|
21
|
+
import storage from '../storage/SessionStorage.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* List scripts
|
|
25
|
+
*/
|
|
26
|
+
export async function listScripts(long = false) {
|
|
27
|
+
try {
|
|
28
|
+
const scripts = (await getScripts()).data.result;
|
|
29
|
+
scripts.sort((a, b) => a.name.localeCompare(b.name));
|
|
30
|
+
if (long) {
|
|
31
|
+
const table = createTable([
|
|
32
|
+
'Name',
|
|
33
|
+
'UUID',
|
|
34
|
+
'Language',
|
|
35
|
+
'Context',
|
|
36
|
+
'Description',
|
|
37
|
+
]);
|
|
38
|
+
const langMap = { JAVASCRIPT: 'JS', GROOVY: 'Groovy' };
|
|
39
|
+
scripts.forEach((script) => {
|
|
40
|
+
table.push([
|
|
41
|
+
wordwrap(script.name, 25, ' '),
|
|
42
|
+
script._id,
|
|
43
|
+
langMap[script.language],
|
|
44
|
+
wordwrap(titleCase(script.context.split('_').join(' ')), 25),
|
|
45
|
+
wordwrap(script.description, 30),
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
printMessage(table.toString(), 'data');
|
|
49
|
+
} else {
|
|
50
|
+
scripts.forEach((script) => {
|
|
51
|
+
printMessage(`${script.name}`, 'data');
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
printMessage(`Error listing scripts - ${error}`, 'error');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Export script to file
|
|
61
|
+
* @param {String} name script name
|
|
62
|
+
* @param {String} file file name
|
|
63
|
+
*/
|
|
64
|
+
export async function exportScriptByName(name, file) {
|
|
65
|
+
let fileName = getTypedFilename(name, 'script');
|
|
66
|
+
if (file) {
|
|
67
|
+
fileName = file;
|
|
68
|
+
}
|
|
69
|
+
const scriptData = (await getScriptByName(name)).data.result;
|
|
70
|
+
if (scriptData.length > 1) {
|
|
71
|
+
printMessage(`Multiple scripts with name ${name} found...`, 'error');
|
|
72
|
+
}
|
|
73
|
+
scriptData.forEach((element) => {
|
|
74
|
+
const scriptTextArray = convertBase64TextToArray(element.script);
|
|
75
|
+
// eslint-disable-next-line no-param-reassign
|
|
76
|
+
element.script = scriptTextArray;
|
|
77
|
+
});
|
|
78
|
+
saveToFile('script', scriptData, '_id', fileName);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Export all scripts to single file
|
|
83
|
+
* @param {String} file file name
|
|
84
|
+
*/
|
|
85
|
+
export async function exportScriptsToFile(file) {
|
|
86
|
+
let fileName = getTypedFilename(
|
|
87
|
+
`all${storage.session.getRealm()}Scripts`,
|
|
88
|
+
'script'
|
|
89
|
+
);
|
|
90
|
+
if (file) {
|
|
91
|
+
fileName = file;
|
|
92
|
+
}
|
|
93
|
+
const scriptList = (await getScripts()).data.result;
|
|
94
|
+
const allScriptsData = [];
|
|
95
|
+
createProgressBar(scriptList.length, 'Exporting script');
|
|
96
|
+
for (const item of scriptList) {
|
|
97
|
+
updateProgressBar(`Reading script ${item.name}`);
|
|
98
|
+
// eslint-disable-next-line no-await-in-loop
|
|
99
|
+
const scriptData = (await getScriptByName(item.name)).data.result;
|
|
100
|
+
scriptData.forEach((element) => {
|
|
101
|
+
const scriptTextArray = convertBase64TextToArray(element.script);
|
|
102
|
+
// eslint-disable-next-line no-param-reassign
|
|
103
|
+
element.script = scriptTextArray;
|
|
104
|
+
allScriptsData.push(element);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
stopProgressBar('Done');
|
|
108
|
+
saveToFile('script', allScriptsData, '_id', fileName);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Export all scripts to individual files
|
|
113
|
+
*/
|
|
114
|
+
export async function exportScriptsToFiles() {
|
|
115
|
+
const scriptList = (await getScripts()).data.result;
|
|
116
|
+
createProgressBar(scriptList.length, 'Exporting script');
|
|
117
|
+
for (const item of scriptList) {
|
|
118
|
+
updateProgressBar(`Reading script ${item.name}`);
|
|
119
|
+
// eslint-disable-next-line no-await-in-loop
|
|
120
|
+
const scriptData = (await getScriptByName(item.name)).data.result;
|
|
121
|
+
scriptData.forEach((element) => {
|
|
122
|
+
const scriptTextArray = convertBase64TextToArray(element.script);
|
|
123
|
+
// eslint-disable-next-line no-param-reassign
|
|
124
|
+
element.script = scriptTextArray;
|
|
125
|
+
});
|
|
126
|
+
const fileName = getTypedFilename(item.name, 'script');
|
|
127
|
+
saveToFile('script', scriptData, '_id', fileName);
|
|
128
|
+
}
|
|
129
|
+
stopProgressBar('Done');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Import script
|
|
134
|
+
* @param {String} id script uuid
|
|
135
|
+
* @param {Object} data script object
|
|
136
|
+
* @returns {Object} a status object
|
|
137
|
+
*/
|
|
138
|
+
export async function createOrUpdateScript(id, data) {
|
|
139
|
+
try {
|
|
140
|
+
await putScript(id, data);
|
|
141
|
+
return { error: false, name: data.name };
|
|
142
|
+
} catch (e) {
|
|
143
|
+
if (e.response.status === 409) {
|
|
144
|
+
printMessage(
|
|
145
|
+
`createOrUpdateScript WARNING: script with name ${data.name} already exists, using renaming policy... <name> => <name - imported (n)>`,
|
|
146
|
+
'warn'
|
|
147
|
+
);
|
|
148
|
+
const newName = applyNameCollisionPolicy(data.name);
|
|
149
|
+
// console.log(newName);
|
|
150
|
+
printMessage(`Trying to save script as ${newName}`, 'warn');
|
|
151
|
+
// eslint-disable-next-line no-param-reassign
|
|
152
|
+
data.name = newName;
|
|
153
|
+
await createOrUpdateScript(id, data);
|
|
154
|
+
return { error: false, name: data.name };
|
|
155
|
+
}
|
|
156
|
+
printMessage(
|
|
157
|
+
`createOrUpdateScript ERROR: put script error, script ${id} - ${e.message}`,
|
|
158
|
+
'error'
|
|
159
|
+
);
|
|
160
|
+
return { error: true, name: data.name };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function importScriptsFromFile(name, file, reUuid = false) {
|
|
165
|
+
fs.readFile(file, 'utf8', (err, data) => {
|
|
166
|
+
if (err) throw err;
|
|
167
|
+
const scriptData = JSON.parse(data);
|
|
168
|
+
if (validateImport(scriptData.meta)) {
|
|
169
|
+
createProgressBar(Object.keys(scriptData.script).length, '');
|
|
170
|
+
for (const existingId in scriptData.script) {
|
|
171
|
+
if ({}.hasOwnProperty.call(scriptData.script, existingId)) {
|
|
172
|
+
let newId = existingId;
|
|
173
|
+
// console.log(id);
|
|
174
|
+
const encodedScript = convertTextArrayToBase64(
|
|
175
|
+
scriptData.script[existingId].script
|
|
176
|
+
);
|
|
177
|
+
scriptData.script[existingId].script = encodedScript;
|
|
178
|
+
if (reUuid) {
|
|
179
|
+
newId = uuidv4();
|
|
180
|
+
// printMessage(
|
|
181
|
+
// `Re-uuid-ing script ${scriptData.script[existingId].name} ${existingId} => ${newId}...`
|
|
182
|
+
// );
|
|
183
|
+
scriptData.script[existingId]._id = newId;
|
|
184
|
+
}
|
|
185
|
+
if (name) {
|
|
186
|
+
// printMessage(
|
|
187
|
+
// `Renaming script ${scriptData.script[existingId].name} => ${options.script}...`
|
|
188
|
+
// );
|
|
189
|
+
scriptData.script[existingId].name = name;
|
|
190
|
+
}
|
|
191
|
+
updateProgressBar(`Importing ${scriptData.script[existingId].name}`);
|
|
192
|
+
// console.log(scriptData.script[id]);
|
|
193
|
+
createOrUpdateScript(newId, scriptData.script[existingId]).then(
|
|
194
|
+
(result) => {
|
|
195
|
+
if (result == null)
|
|
196
|
+
printMessage(
|
|
197
|
+
`Error importing ${scriptData.script[existingId].name}`,
|
|
198
|
+
'error'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
if (name) break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
stopProgressBar('Done');
|
|
206
|
+
// printMessage('Done');
|
|
207
|
+
} else {
|
|
208
|
+
printMessage('Import validation failed...', 'error');
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createKeyValueTable,
|
|
3
|
+
createProgressBar,
|
|
4
|
+
createTable,
|
|
5
|
+
failSpinner,
|
|
6
|
+
printMessage,
|
|
7
|
+
showSpinner,
|
|
8
|
+
stopProgressBar,
|
|
9
|
+
succeedSpinner,
|
|
10
|
+
updateProgressBar,
|
|
11
|
+
} from './utils/Console.js';
|
|
12
|
+
import {
|
|
13
|
+
createNewVersionOfSecret,
|
|
14
|
+
deleteSecret,
|
|
15
|
+
deleteVersionOfSecret,
|
|
16
|
+
getSecret,
|
|
17
|
+
getSecrets,
|
|
18
|
+
getSecretVersions,
|
|
19
|
+
putSecret,
|
|
20
|
+
setSecretDescription,
|
|
21
|
+
setStatusOfVersionOfSecret,
|
|
22
|
+
} from '../api/SecretsApi.js';
|
|
23
|
+
import wordwrap from './utils/Wordwrap.js';
|
|
24
|
+
import { resolveUserName } from './ManagedObjectOps.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* List secrets
|
|
28
|
+
* @param {boolean} long Long version, all the fields
|
|
29
|
+
*/
|
|
30
|
+
export async function listSecrets(long) {
|
|
31
|
+
let secrets = [];
|
|
32
|
+
try {
|
|
33
|
+
secrets = (await getSecrets()).data.result;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
printMessage(`${error.message}`, 'error');
|
|
36
|
+
printMessage(error.response.data, 'error');
|
|
37
|
+
}
|
|
38
|
+
if (long) {
|
|
39
|
+
const table = createTable([
|
|
40
|
+
'Id'.brightCyan,
|
|
41
|
+
{ hAlign: 'right', content: 'Active\nVersion'.brightCyan },
|
|
42
|
+
{ hAlign: 'right', content: 'Loaded\nVersion'.brightCyan },
|
|
43
|
+
'Status'.brightCyan,
|
|
44
|
+
'Description'.brightCyan,
|
|
45
|
+
'Modifier'.brightCyan,
|
|
46
|
+
'Modified'.brightCyan,
|
|
47
|
+
]);
|
|
48
|
+
secrets.sort((a, b) => a._id.localeCompare(b._id));
|
|
49
|
+
for (const secret of secrets) {
|
|
50
|
+
table.push([
|
|
51
|
+
secret._id,
|
|
52
|
+
{ hAlign: 'right', content: secret.activeVersion },
|
|
53
|
+
{ hAlign: 'right', content: secret.loadedVersion },
|
|
54
|
+
secret.loaded ? 'loaded'.brightGreen : 'unloaded'.brightRed,
|
|
55
|
+
wordwrap(secret.description, 40),
|
|
56
|
+
// eslint-disable-next-line no-await-in-loop
|
|
57
|
+
await resolveUserName('teammember', secret.lastChangedBy),
|
|
58
|
+
new Date(secret.lastChangeDate).toLocaleString(),
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
printMessage(table.toString());
|
|
62
|
+
} else {
|
|
63
|
+
secrets.forEach((secret) => {
|
|
64
|
+
printMessage(secret._id);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create secret
|
|
71
|
+
* @param {String} id secret id
|
|
72
|
+
* @param {String} value secret value
|
|
73
|
+
* @param {String} description secret description
|
|
74
|
+
* @param {String} encoding secret encoding
|
|
75
|
+
* @param {boolean} useInPlaceholders use secret in placeholders
|
|
76
|
+
*/
|
|
77
|
+
export async function createSecret(
|
|
78
|
+
id,
|
|
79
|
+
value,
|
|
80
|
+
description,
|
|
81
|
+
encoding,
|
|
82
|
+
useInPlaceholders
|
|
83
|
+
) {
|
|
84
|
+
showSpinner(`Creating secret ${id}...`);
|
|
85
|
+
try {
|
|
86
|
+
await putSecret(id, value, description, encoding, useInPlaceholders);
|
|
87
|
+
succeedSpinner(`Created secret ${id}`);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
failSpinner(
|
|
90
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set description of secret
|
|
97
|
+
* @param {String} secretId secret id
|
|
98
|
+
* @param {String} description secret description
|
|
99
|
+
*/
|
|
100
|
+
export async function setDescriptionOfSecret(secretId, description) {
|
|
101
|
+
showSpinner(`Setting description of secret ${secretId}...`);
|
|
102
|
+
try {
|
|
103
|
+
await setSecretDescription(secretId, description);
|
|
104
|
+
succeedSpinner(`Set description of secret ${secretId}`);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
failSpinner(
|
|
107
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Delete a secret
|
|
114
|
+
* @param {String} secretId secret id
|
|
115
|
+
*/
|
|
116
|
+
export async function deleteSecretCmd(secretId) {
|
|
117
|
+
showSpinner(`Deleting secret ${secretId}...`);
|
|
118
|
+
try {
|
|
119
|
+
await deleteSecret(secretId);
|
|
120
|
+
succeedSpinner(`Deleted secret ${secretId}`);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
failSpinner(
|
|
123
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Delete all secrets
|
|
130
|
+
*/
|
|
131
|
+
export async function deleteSecretsCmd() {
|
|
132
|
+
try {
|
|
133
|
+
const secrets = (await getSecrets()).data.result;
|
|
134
|
+
createProgressBar(secrets.length, `Deleting secrets...`);
|
|
135
|
+
for (const secret of secrets) {
|
|
136
|
+
try {
|
|
137
|
+
// eslint-disable-next-line no-await-in-loop
|
|
138
|
+
await deleteSecret(secret._id);
|
|
139
|
+
updateProgressBar(`Deleted secret ${secret._id}`);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
printMessage(
|
|
142
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`,
|
|
143
|
+
'error'
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
stopProgressBar(`Secrets deleted.`);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
printMessage(
|
|
150
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`,
|
|
151
|
+
'error'
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* List all the versions of the secret
|
|
158
|
+
* @param {String} secretId secret id
|
|
159
|
+
*/
|
|
160
|
+
export async function listSecretVersionsCmd(secretId) {
|
|
161
|
+
let versions = [];
|
|
162
|
+
try {
|
|
163
|
+
versions = (await getSecretVersions(secretId)).data;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
printMessage(`${error.message}`, 'error');
|
|
166
|
+
printMessage(error.response.data, 'error');
|
|
167
|
+
}
|
|
168
|
+
const table = createTable([
|
|
169
|
+
{ hAlign: 'right', content: 'Version'.brightCyan },
|
|
170
|
+
'Status'.brightCyan,
|
|
171
|
+
'Loaded'.brightCyan,
|
|
172
|
+
'Created'.brightCyan,
|
|
173
|
+
]);
|
|
174
|
+
// versions.sort((a, b) => a._id.localeCompare(b._id));
|
|
175
|
+
const statusMap = {
|
|
176
|
+
ENABLED: 'active'.brightGreen,
|
|
177
|
+
DISABLED: 'inactive'.brightRed,
|
|
178
|
+
DESTROYED: 'deleted'.brightRed,
|
|
179
|
+
};
|
|
180
|
+
versions.forEach((version) => {
|
|
181
|
+
table.push([
|
|
182
|
+
{ hAlign: 'right', content: version.version },
|
|
183
|
+
statusMap[version.status],
|
|
184
|
+
version.loaded ? 'loaded'.brightGreen : 'unloaded'.brightRed,
|
|
185
|
+
new Date(version.createDate).toLocaleString(),
|
|
186
|
+
]);
|
|
187
|
+
});
|
|
188
|
+
printMessage(table.toString());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Describe a secret
|
|
193
|
+
* @param {String} secretId Secret id
|
|
194
|
+
*/
|
|
195
|
+
export async function describeSecret(secretId) {
|
|
196
|
+
const secret = (await getSecret(secretId)).data;
|
|
197
|
+
const table = createKeyValueTable();
|
|
198
|
+
table.push(['Name'.brightCyan, secret._id]);
|
|
199
|
+
table.push(['Active Version'.brightCyan, secret.activeVersion]);
|
|
200
|
+
table.push(['Loaded Version'.brightCyan, secret.loadedVersion]);
|
|
201
|
+
table.push([
|
|
202
|
+
'Status'.brightCyan,
|
|
203
|
+
secret.loaded ? 'loaded'.brightGreen : 'unloaded'.brightRed,
|
|
204
|
+
]);
|
|
205
|
+
table.push(['Description'.brightCyan, wordwrap(secret.description, 60)]);
|
|
206
|
+
table.push([
|
|
207
|
+
'Modified'.brightCyan,
|
|
208
|
+
new Date(secret.lastChangeDate).toLocaleString(),
|
|
209
|
+
]);
|
|
210
|
+
table.push([
|
|
211
|
+
'Modifier'.brightCyan,
|
|
212
|
+
await resolveUserName('teammember', secret.lastChangedBy),
|
|
213
|
+
]);
|
|
214
|
+
table.push(['Modifier UUID'.brightCyan, secret.lastChangedBy]);
|
|
215
|
+
table.push(['Encoding'.brightCyan, secret.encoding]);
|
|
216
|
+
table.push(['Use In Placeholders'.brightCyan, secret.useInPlaceholders]);
|
|
217
|
+
printMessage(table.toString());
|
|
218
|
+
printMessage('\nSecret Versions:');
|
|
219
|
+
await listSecretVersionsCmd(secretId);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Create new version of secret
|
|
224
|
+
* @param {String} secretId secret id
|
|
225
|
+
* @param {String} value secret value
|
|
226
|
+
*/
|
|
227
|
+
export async function createNewVersionOfSecretCmd(secretId, value) {
|
|
228
|
+
showSpinner(`Creating new version of secret ${secretId}...`);
|
|
229
|
+
try {
|
|
230
|
+
const version = (await createNewVersionOfSecret(secretId, value)).data;
|
|
231
|
+
succeedSpinner(`Created version ${version.version} of secret ${secretId}`);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
failSpinner(
|
|
234
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Activate a version of a secret
|
|
241
|
+
* @param {String} secretId secret id
|
|
242
|
+
* @param {Number} version version of secret
|
|
243
|
+
*/
|
|
244
|
+
export async function activateVersionOfSecret(secretId, version) {
|
|
245
|
+
showSpinner(`Activating version ${version} of secret ${secretId}...`);
|
|
246
|
+
try {
|
|
247
|
+
await setStatusOfVersionOfSecret(secretId, version, 'ENABLED');
|
|
248
|
+
succeedSpinner(`Activated version ${version} of secret ${secretId}`);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
failSpinner(
|
|
251
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Deactivate a version of a secret
|
|
258
|
+
* @param {String} secretId secret id
|
|
259
|
+
* @param {Number} version version of secret
|
|
260
|
+
*/
|
|
261
|
+
export async function deactivateVersionOfSecret(secretId, version) {
|
|
262
|
+
showSpinner(`Deactivating version ${version} of secret ${secretId}...`);
|
|
263
|
+
try {
|
|
264
|
+
await setStatusOfVersionOfSecret(secretId, version, 'DISABLED');
|
|
265
|
+
succeedSpinner(`Deactivated version ${version} of secret ${secretId}`);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
failSpinner(
|
|
268
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Delete version of secret
|
|
275
|
+
* @param {String} secretId secret id
|
|
276
|
+
* @param {Number} version version of secret
|
|
277
|
+
*/
|
|
278
|
+
export async function deleteVersionOfSecretCmd(secretId, version) {
|
|
279
|
+
showSpinner(`Deleting version ${version} of secret ${secretId}...`);
|
|
280
|
+
try {
|
|
281
|
+
await deleteVersionOfSecret(secretId, version);
|
|
282
|
+
succeedSpinner(`Deleted version ${version} of secret ${secretId}`);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
failSpinner(
|
|
285
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import yesno from 'yesno';
|
|
2
|
+
import {
|
|
3
|
+
createTable,
|
|
4
|
+
failSpinner,
|
|
5
|
+
printMessage,
|
|
6
|
+
showSpinner,
|
|
7
|
+
spinSpinner,
|
|
8
|
+
succeedSpinner,
|
|
9
|
+
} from './utils/Console.js';
|
|
10
|
+
import { getSecrets } from '../api/SecretsApi.js';
|
|
11
|
+
import { getStatus, initiateRestart } from '../api/StartupApi.js';
|
|
12
|
+
import { resolveUserName } from './ManagedObjectOps.js';
|
|
13
|
+
import { getVariables } from '../api/VariablesApi.js';
|
|
14
|
+
|
|
15
|
+
export async function checkForUpdates() {
|
|
16
|
+
showSpinner(`Checking for updates to apply...`);
|
|
17
|
+
const updates = createTable([
|
|
18
|
+
'Type'.brightCyan,
|
|
19
|
+
'Name'.brightCyan,
|
|
20
|
+
'Modified'.brightCyan,
|
|
21
|
+
'Modifier'.brightCyan,
|
|
22
|
+
]);
|
|
23
|
+
let secrets = [];
|
|
24
|
+
let variables = [];
|
|
25
|
+
try {
|
|
26
|
+
secrets = (await getSecrets()).data.result;
|
|
27
|
+
for (const secret of secrets) {
|
|
28
|
+
if (!secret.loaded) {
|
|
29
|
+
updates.push([
|
|
30
|
+
'secret',
|
|
31
|
+
secret._id,
|
|
32
|
+
new Date(secret.lastChangeDate).toLocaleString(),
|
|
33
|
+
// eslint-disable-next-line no-await-in-loop
|
|
34
|
+
await resolveUserName('teammember', secret.lastChangedBy),
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
variables = (await getVariables()).data.result;
|
|
39
|
+
for (const variable of variables) {
|
|
40
|
+
if (!variable.loaded) {
|
|
41
|
+
updates.push([
|
|
42
|
+
'variable',
|
|
43
|
+
variable._id,
|
|
44
|
+
new Date(variable.lastChangeDate).toLocaleString(),
|
|
45
|
+
// eslint-disable-next-line no-await-in-loop
|
|
46
|
+
await resolveUserName('teammember', variable.lastChangedBy),
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
failSpinner(
|
|
52
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (updates.length > 0) {
|
|
56
|
+
succeedSpinner(`${updates.length} update(s) need to be applied`);
|
|
57
|
+
printMessage(updates.toString(), 'data');
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
succeedSpinner(`No updates need to be applied`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function applyUpdates(force, wait, yes) {
|
|
65
|
+
if ((await checkForUpdates()) || force) {
|
|
66
|
+
const ok =
|
|
67
|
+
yes ||
|
|
68
|
+
(await yesno({
|
|
69
|
+
question: `\nChanges may take up to 10 minutes to propagate, during which time you will not be able to make further updates.\n\nApply updates? (y|n):`,
|
|
70
|
+
}));
|
|
71
|
+
if (ok) {
|
|
72
|
+
showSpinner(`Applying updates...`);
|
|
73
|
+
try {
|
|
74
|
+
await initiateRestart();
|
|
75
|
+
if (wait) {
|
|
76
|
+
const timeout = 10 * 60 * 1000;
|
|
77
|
+
const start = new Date().getTime();
|
|
78
|
+
let runtime = 0;
|
|
79
|
+
// eslint-disable-next-line no-await-in-loop
|
|
80
|
+
let status = (await getStatus()).data.restartStatus;
|
|
81
|
+
while (status !== 'ready' && start + timeout > new Date().getTime()) {
|
|
82
|
+
// eslint-disable-next-line no-await-in-loop, no-promise-executor-return
|
|
83
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
84
|
+
// eslint-disable-next-line no-await-in-loop
|
|
85
|
+
status = (await getStatus()).data.restartStatus;
|
|
86
|
+
runtime = new Date().getTime() - start;
|
|
87
|
+
spinSpinner(`${status} (${Math.round(runtime / 1000)}s)`);
|
|
88
|
+
}
|
|
89
|
+
if (runtime < timeout) {
|
|
90
|
+
succeedSpinner(
|
|
91
|
+
`Updates applied in ${Math.round(
|
|
92
|
+
runtime / 1000
|
|
93
|
+
)}s with final status: ${status}`
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
succeedSpinner(
|
|
97
|
+
`Updates timed out after ${Math.round(
|
|
98
|
+
runtime / 1000
|
|
99
|
+
)}s with final status: ${status}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
succeedSpinner(
|
|
104
|
+
`Updates are being applied. Changes may take up to 10 minutes to propagate, during which time you will not be able to make further updates.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
failSpinner(
|
|
109
|
+
`Error: ${error.response.data.code} - ${error.response.data.message}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|