@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.
Files changed (112) hide show
  1. package/.eslintrc +32 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. package/.github/README.md +121 -0
  5. package/.github/workflows/pipeline.yml +287 -0
  6. package/.prettierrc +6 -0
  7. package/CHANGELOG.md +512 -0
  8. package/CODE_OF_CONDUCT.md +128 -0
  9. package/LICENSE +21 -0
  10. package/README.md +8 -0
  11. package/docs/CONTRIBUTE.md +96 -0
  12. package/docs/PIPELINE.md +169 -0
  13. package/docs/images/npm_versioning_guidelines.png +0 -0
  14. package/docs/images/release_pipeline.png +0 -0
  15. package/jsconfig.json +6 -0
  16. package/package.json +95 -0
  17. package/resources/sampleEntitiesFile.json +8 -0
  18. package/resources/sampleEnvFile.env +2 -0
  19. package/src/api/AuthenticateApi.js +33 -0
  20. package/src/api/BaseApi.js +242 -0
  21. package/src/api/CirclesOfTrustApi.js +87 -0
  22. package/src/api/EmailTemplateApi.js +37 -0
  23. package/src/api/IdmConfigApi.js +88 -0
  24. package/src/api/LogApi.js +45 -0
  25. package/src/api/ManagedObjectApi.js +62 -0
  26. package/src/api/OAuth2ClientApi.js +69 -0
  27. package/src/api/OAuth2OIDCApi.js +73 -0
  28. package/src/api/OAuth2ProviderApi.js +32 -0
  29. package/src/api/RealmApi.js +99 -0
  30. package/src/api/Saml2Api.js +176 -0
  31. package/src/api/ScriptApi.js +84 -0
  32. package/src/api/SecretsApi.js +151 -0
  33. package/src/api/ServerInfoApi.js +41 -0
  34. package/src/api/SocialIdentityProvidersApi.js +114 -0
  35. package/src/api/StartupApi.js +45 -0
  36. package/src/api/ThemeApi.js +181 -0
  37. package/src/api/TreeApi.js +207 -0
  38. package/src/api/VariablesApi.js +104 -0
  39. package/src/api/utils/ApiUtils.js +77 -0
  40. package/src/api/utils/ApiUtils.test.js +96 -0
  41. package/src/api/utils/Base64.js +62 -0
  42. package/src/index.js +32 -0
  43. package/src/index.test.js +13 -0
  44. package/src/ops/AdminOps.js +901 -0
  45. package/src/ops/AuthenticateOps.js +342 -0
  46. package/src/ops/CirclesOfTrustOps.js +350 -0
  47. package/src/ops/ConnectionProfileOps.js +254 -0
  48. package/src/ops/EmailTemplateOps.js +326 -0
  49. package/src/ops/IdmOps.js +227 -0
  50. package/src/ops/IdpOps.js +342 -0
  51. package/src/ops/JourneyOps.js +2026 -0
  52. package/src/ops/LogOps.js +357 -0
  53. package/src/ops/ManagedObjectOps.js +34 -0
  54. package/src/ops/OAuth2ClientOps.js +151 -0
  55. package/src/ops/OrganizationOps.js +85 -0
  56. package/src/ops/RealmOps.js +139 -0
  57. package/src/ops/SamlOps.js +541 -0
  58. package/src/ops/ScriptOps.js +211 -0
  59. package/src/ops/SecretsOps.js +288 -0
  60. package/src/ops/StartupOps.js +114 -0
  61. package/src/ops/ThemeOps.js +379 -0
  62. package/src/ops/VariablesOps.js +185 -0
  63. package/src/ops/templates/OAuth2ClientTemplate.json +270 -0
  64. package/src/ops/templates/OrgModelUserAttributesTemplate.json +149 -0
  65. package/src/ops/templates/cloud/GenericExtensionAttributesTemplate.json +392 -0
  66. package/src/ops/templates/cloud/managed.json +4119 -0
  67. package/src/ops/utils/Console.js +434 -0
  68. package/src/ops/utils/DataProtection.js +92 -0
  69. package/src/ops/utils/DataProtection.test.js +28 -0
  70. package/src/ops/utils/ExportImportUtils.js +146 -0
  71. package/src/ops/utils/ExportImportUtils.test.js +119 -0
  72. package/src/ops/utils/OpsUtils.js +76 -0
  73. package/src/ops/utils/Wordwrap.js +11 -0
  74. package/src/storage/SessionStorage.js +45 -0
  75. package/src/storage/StaticStorage.js +15 -0
  76. package/test/e2e/journey/baseline/ForgottenUsername.journey.json +216 -0
  77. package/test/e2e/journey/baseline/Login.journey.json +205 -0
  78. package/test/e2e/journey/baseline/PasswordGrant.journey.json +139 -0
  79. package/test/e2e/journey/baseline/ProgressiveProfile.journey.json +198 -0
  80. package/test/e2e/journey/baseline/Registration.journey.json +249 -0
  81. package/test/e2e/journey/baseline/ResetPassword.journey.json +268 -0
  82. package/test/e2e/journey/baseline/UpdatePassword.journey.json +323 -0
  83. package/test/e2e/journey/baseline/allAlphaJourneys.journeys.json +1520 -0
  84. package/test/e2e/journey/delete/ForgottenUsername.journey.json +216 -0
  85. package/test/e2e/journey/delete/Login.journey.json +205 -0
  86. package/test/e2e/journey/delete/PasswordGrant.journey.json +139 -0
  87. package/test/e2e/journey/delete/ProgressiveProfile.journey.json +198 -0
  88. package/test/e2e/journey/delete/Registration.journey.json +249 -0
  89. package/test/e2e/journey/delete/ResetPassword.journey.json +268 -0
  90. package/test/e2e/journey/delete/UpdatePassword.journey.json +323 -0
  91. package/test/e2e/journey/delete/deleteMe.journey.json +230 -0
  92. package/test/e2e/journey/list/Disabled.journey.json +43 -0
  93. package/test/e2e/journey/list/ForgottenUsername.journey.json +216 -0
  94. package/test/e2e/journey/list/Login.journey.json +205 -0
  95. package/test/e2e/journey/list/PasswordGrant.journey.json +139 -0
  96. package/test/e2e/journey/list/ProgressiveProfile.journey.json +198 -0
  97. package/test/e2e/journey/list/Registration.journey.json +249 -0
  98. package/test/e2e/journey/list/ResetPassword.journey.json +268 -0
  99. package/test/e2e/journey/list/UpdatePassword.journey.json +323 -0
  100. package/test/e2e/setup.js +107 -0
  101. package/test/e2e/theme/baseline/Contrast.theme.json +95 -0
  102. package/test/e2e/theme/baseline/Highlander.theme.json +95 -0
  103. package/test/e2e/theme/baseline/Robroy.theme.json +95 -0
  104. package/test/e2e/theme/baseline/Starter-Theme.theme.json +94 -0
  105. package/test/e2e/theme/baseline/Zardoz.theme.json +95 -0
  106. package/test/e2e/theme/import/Contrast.theme.json +95 -0
  107. package/test/e2e/theme/import/Highlander.theme.json +95 -0
  108. package/test/e2e/theme/import/Robroy.theme.json +95 -0
  109. package/test/e2e/theme/import/Starter-Theme.theme.json +94 -0
  110. package/test/e2e/theme/import/Zardoz.default.theme.json +95 -0
  111. package/test/fs_tmp/.gitkeep +2 -0
  112. package/test/global/setup.js +65 -0
@@ -0,0 +1,357 @@
1
+ import { printMessage } from './utils/Console.js';
2
+ import { getCurrentTimestamp } from './utils/ExportImportUtils.js';
3
+ import {
4
+ createAPIKeyAndSecret,
5
+ getAPIKeys,
6
+ getSources,
7
+ } from '../api/LogApi.js';
8
+
9
+ import storage from '../storage/SessionStorage.js';
10
+
11
+ import * as LogApi from '../api/LogApi.js';
12
+
13
+ const unfilterableNoise = [
14
+ 'text/plain' // Unfortunately, it is impossible to filter out those without excluding IDM script logging as well
15
+ ]
16
+
17
+ const miscNoise = [
18
+ 'com.iplanet.dpro.session.operations.ServerSessionOperationStrategy',
19
+ 'com.iplanet.dpro.session.SessionIDFactory',
20
+ 'com.iplanet.dpro.session.share.SessionEncodeURL',
21
+ 'com.iplanet.services.naming.WebtopNaming',
22
+ 'com.iplanet.sso.providers.dpro.SSOProviderImpl',
23
+ 'com.sun.identity.authentication.AuthContext',
24
+ 'com.sun.identity.authentication.client.AuthClientUtils',
25
+ 'com.sun.identity.authentication.config.AMAuthConfigType',
26
+ 'com.sun.identity.authentication.config.AMAuthenticationManager',
27
+ 'com.sun.identity.authentication.config.AMAuthLevelManager',
28
+ 'com.sun.identity.authentication.config.AMConfiguration',
29
+ 'com.sun.identity.authentication.jaas.LoginContext',
30
+ 'com.sun.identity.authentication.modules.application.Application',
31
+ 'com.sun.identity.authentication.server.AuthContextLocal',
32
+ 'com.sun.identity.authentication.service.AMLoginContext',
33
+ 'com.sun.identity.authentication.service.AuthContextLookup',
34
+ 'com.sun.identity.authentication.service.AuthD',
35
+ 'com.sun.identity.authentication.service.AuthUtils',
36
+ 'com.sun.identity.authentication.service.DSAMECallbackHandler',
37
+ 'com.sun.identity.authentication.service.LoginState',
38
+ 'com.sun.identity.authentication.spi.AMLoginModule',
39
+ 'com.sun.identity.delegation.DelegationEvaluatorImpl',
40
+ 'com.sun.identity.idm.plugins.internal.AgentsRepo',
41
+ 'com.sun.identity.idm.server.IdCachedServicesImpl',
42
+ 'com.sun.identity.idm.server.IdRepoPluginsCache',
43
+ 'com.sun.identity.idm.server.IdServicesImpl',
44
+ 'com.sun.identity.log.spi.ISDebug',
45
+ 'com.sun.identity.shared.encode.CookieUtils',
46
+ 'com.sun.identity.sm.ldap.SMSLdapObject',
47
+ 'com.sun.identity.sm.CachedSMSEntry',
48
+ 'com.sun.identity.sm.CachedSubEntries',
49
+ 'com.sun.identity.sm.DNMapper',
50
+ 'com.sun.identity.sm.ServiceConfigImpl',
51
+ 'com.sun.identity.sm.ServiceConfigManagerImpl',
52
+ 'com.sun.identity.sm.SMSEntry',
53
+ 'com.sun.identity.sm.SMSUtils',
54
+ 'com.sun.identity.sm.SmsWrapperObject',
55
+ 'oauth2',
56
+ 'org.apache.http.client.protocol.RequestAuthCache',
57
+ 'org.apache.http.impl.conn.PoolingHttpClientConnectionManager',
58
+ 'org.apache.http.impl.nio.client.InternalHttpAsyncClient',
59
+ 'org.apache.http.impl.nio.client.InternalIODispatch',
60
+ 'org.apache.http.impl.nio.client.MainClientExec',
61
+ 'org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionImpl',
62
+ 'org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager',
63
+ 'org.forgerock.audit.AuditServiceImpl',
64
+ 'org.forgerock.oauth2.core.RealmOAuth2ProviderSettings',
65
+ 'org.forgerock.openam.authentication.service.JAASModuleDetector',
66
+ 'org.forgerock.openam.authentication.service.LoginContextFactory',
67
+ 'org.forgerock.openam.blacklist.BloomFilterBlacklist',
68
+ 'org.forgerock.openam.blacklist.CTSBlacklist',
69
+ 'org.forgerock.openam.core.realms.impl.CachingRealmLookup',
70
+ 'org.forgerock.openam.core.rest.authn.RestAuthCallbackHandlerManager',
71
+ 'org.forgerock.openam.core.rest.authn.trees.AuthTrees',
72
+ 'org.forgerock.openam.cors.CorsFilter',
73
+ 'org.forgerock.openam.cts.CTSPersistentStoreImpl',
74
+ 'org.forgerock.openam.cts.impl.CoreTokenAdapter',
75
+ 'org.forgerock.openam.cts.impl.queue.AsyncResultHandler',
76
+ 'org.forgerock.openam.cts.reaper.ReaperDeleteOnQueryResultHandler',
77
+ 'org.forgerock.openam.headers.DisableSameSiteCookiesFilter',
78
+ 'org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo',
79
+ 'org.forgerock.openam.rest.CsrfFilter',
80
+ 'org.forgerock.openam.rest.restAuthenticationFilter',
81
+ 'org.forgerock.openam.rest.fluent.CrestLoggingFilter',
82
+ 'org.forgerock.openam.session.cts.CtsOperations',
83
+ 'org.forgerock.openam.session.stateless.StatelessSessionManager',
84
+ 'org.forgerock.openam.sm.datalayer.impl.ldap.ExternalLdapConfig',
85
+ 'org.forgerock.openam.sm.datalayer.impl.ldap.LdapQueryBuilder',
86
+ 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutor',
87
+ 'org.forgerock.openam.sm.datalayer.impl.SeriesTaskExecutorThread',
88
+ 'org.forgerock.openam.sm.datalayer.providers.LdapConnectionFactoryProvider',
89
+ 'org.forgerock.openam.sm.file.ConfigFileSystemHandler',
90
+ 'org.forgerock.openam.social.idp.SocialIdentityProviders',
91
+ 'org.forgerock.openam.utils.ClientUtils',
92
+ 'org.forgerock.opendj.ldap.CachedConnectionPool',
93
+ 'org.forgerock.opendj.ldap.LoadBalancer',
94
+ 'org.forgerock.secrets.keystore.KeyStoreSecretStore',
95
+ 'org.forgerock.secrets.propertyresolver.PropertyResolverSecretStore',
96
+ 'org.forgerock.secrets.SecretsProvider',
97
+ ];
98
+
99
+ const journeysNoise = [
100
+ 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',
101
+ ];
102
+
103
+ // eslint-disable-next-line no-unused-vars
104
+ const journeys = [
105
+ 'org.forgerock.openam.auth.nodes.SelectIdPNode',
106
+ 'org.forgerock.openam.auth.nodes.ValidatedPasswordNode',
107
+ 'org.forgerock.openam.auth.nodes.ValidatedUsernameNode',
108
+ 'org.forgerock.openam.auth.trees.engine.AuthTreeExecutor',
109
+ ];
110
+
111
+ const samlNoise = [
112
+ 'com.sun.identity.cot.COTCache',
113
+ 'com.sun.identity.plugin.configuration.impl.ConfigurationInstanceImpl',
114
+ 'com.sun.identity.saml2.meta.SAML2MetaCache',
115
+ 'com.sun.identity.saml2.profile.CacheCleanUpRunnable',
116
+ 'org.apache.xml.security.keys.KeyInfo',
117
+ 'org.apache.xml.security.signature.XMLSignature',
118
+ 'org.apache.xml.security.utils.SignerOutputStream',
119
+ 'org.apache.xml.security.utils.resolver.ResourceResolver',
120
+ 'org.apache.xml.security.utils.resolver.implementations.ResolverFragment',
121
+ 'org.apache.xml.security.algorithms.JCEMapper',
122
+ 'org.apache.xml.security.algorithms.implementations.SignatureBaseRSA',
123
+ 'org.apache.xml.security.algorithms.SignatureAlgorithm',
124
+ 'org.apache.xml.security.utils.ElementProxy',
125
+ 'org.apache.xml.security.transforms.Transforms',
126
+ 'org.apache.xml.security.utils.DigesterOutputStream',
127
+ 'org.apache.xml.security.signature.Reference',
128
+ 'org.apache.xml.security.signature.Manifest',
129
+ ];
130
+
131
+ // eslint-disable-next-line no-unused-vars
132
+ const saml = [
133
+ 'jsp.saml2.spAssertionConsumer',
134
+ 'com.sun.identity.saml.common.SAMLUtils',
135
+ 'com.sun.identity.saml2.common.SAML2Utils',
136
+ 'com.sun.identity.saml2.meta.SAML2MetaManager',
137
+ 'com.sun.identity.saml2.xmlsig.FMSigProvider',
138
+ ];
139
+
140
+ const noise = miscNoise.concat(samlNoise).concat(journeysNoise);
141
+
142
+ const numLogLevelMap = {
143
+ 0: ['SEVERE', 'ERROR', 'FATAL'],
144
+ 1: ['WARNING', 'WARN', 'CONFIG'],
145
+ 2: ['INFO', 'INFORMATION'],
146
+ 3: ['DEBUG', 'FINE', 'FINER', 'FINEST'],
147
+ 4: ['ALL'],
148
+ };
149
+
150
+ const logLevelMap = {
151
+ SEVERE: ['SEVERE', 'ERROR', 'FATAL'],
152
+ ERROR: ['SEVERE', 'ERROR', 'FATAL'],
153
+ FATAL: ['SEVERE', 'ERROR', 'FATAL'],
154
+ WARN: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],
155
+ WARNING: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],
156
+ CONFIG: ['SEVERE', 'ERROR', 'FATAL', 'WARNING', 'WARN', 'CONFIG'],
157
+ INFO: [
158
+ 'SEVERE',
159
+ 'ERROR',
160
+ 'FATAL',
161
+ 'WARNING',
162
+ 'WARN',
163
+ 'CONFIG',
164
+ 'INFO',
165
+ 'INFORMATION',
166
+ ],
167
+ INFORMATION: [
168
+ 'SEVERE',
169
+ 'ERROR',
170
+ 'FATAL',
171
+ 'WARNING',
172
+ 'WARN',
173
+ 'CONFIG',
174
+ 'INFO',
175
+ 'INFORMATION',
176
+ ],
177
+ DEBUG: [
178
+ 'SEVERE',
179
+ 'ERROR',
180
+ 'FATAL',
181
+ 'WARNING',
182
+ 'WARN',
183
+ 'CONFIG',
184
+ 'INFO',
185
+ 'INFORMATION',
186
+ 'DEBUG',
187
+ 'FINE',
188
+ 'FINER',
189
+ 'FINEST',
190
+ ],
191
+ FINE: [
192
+ 'SEVERE',
193
+ 'ERROR',
194
+ 'FATAL',
195
+ 'WARNING',
196
+ 'WARN',
197
+ 'CONFIG',
198
+ 'INFO',
199
+ 'INFORMATION',
200
+ 'DEBUG',
201
+ 'FINE',
202
+ 'FINER',
203
+ 'FINEST',
204
+ ],
205
+ FINER: [
206
+ 'SEVERE',
207
+ 'ERROR',
208
+ 'FATAL',
209
+ 'WARNING',
210
+ 'WARN',
211
+ 'CONFIG',
212
+ 'INFO',
213
+ 'INFORMATION',
214
+ 'DEBUG',
215
+ 'FINE',
216
+ 'FINER',
217
+ 'FINEST',
218
+ ],
219
+ FINEST: [
220
+ 'SEVERE',
221
+ 'ERROR',
222
+ 'FATAL',
223
+ 'WARNING',
224
+ 'WARN',
225
+ 'CONFIG',
226
+ 'INFO',
227
+ 'INFORMATION',
228
+ 'DEBUG',
229
+ 'FINE',
230
+ 'FINER',
231
+ 'FINEST',
232
+ ],
233
+ ALL: ['ALL'],
234
+ };
235
+
236
+ export function resolveLevel(level) {
237
+ // const levels = ['FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL'];
238
+ // levels.splice(levels.indexOf(levelName) + 1, levels.length);
239
+ if (Number.isNaN(parseInt(level, 10))) {
240
+ return logLevelMap[level];
241
+ }
242
+ return logLevelMap[numLogLevelMap[level][0]];
243
+ }
244
+
245
+ // It seems that the undesirable 'text/plain' logs start with a date, not a LEVEL
246
+ // Therefore, for those, this function returns null, and thus filters out the undesirable
247
+ export function resolvePayloadLevel(log) {
248
+ try {
249
+ return log.type !== 'text/plain' ? log.payload.level : log.payload.match(/^([^:]*):/)[1];
250
+ } catch (e) { // Fail-safe for no group match
251
+ return null
252
+ }
253
+ }
254
+
255
+ export async function getLogSources() {
256
+ const sources = [];
257
+ await getSources()
258
+ .then((response) => {
259
+ response.data.result.forEach((item) => {
260
+ sources.push(item);
261
+ });
262
+ })
263
+ .catch((error) => {
264
+ printMessage(
265
+ `getSources ERROR: get log sources call returned ${error}}`,
266
+ 'error'
267
+ );
268
+ return [];
269
+ });
270
+ return sources;
271
+ }
272
+
273
+ export async function tailLogs(source, levels, txid, cookie) {
274
+ try {
275
+ const response = await LogApi.tail(source, cookie);
276
+ if (response.status < 200 || response.status > 399) {
277
+ printMessage(
278
+ `tail ERROR: tail call returned ${response.status}`,
279
+ 'error'
280
+ );
281
+ return null;
282
+ }
283
+ // if (!cookie) {
284
+ // await saveConnection();
285
+ // }
286
+ const logsObject = response.data;
287
+ let filteredLogs = [];
288
+ if (Array.isArray(logsObject.result)) {
289
+ filteredLogs = logsObject.result.filter(
290
+ (el) =>
291
+ !noise.includes(el.payload.logger) &&
292
+ !noise.includes(el.type) &&
293
+ (levels[0] === 'ALL' || levels.includes(resolvePayloadLevel(el))) &&
294
+ (typeof txid === 'undefined' ||
295
+ txid === null ||
296
+ el.payload.transactionId.includes(txid))
297
+ );
298
+ }
299
+
300
+ filteredLogs.forEach((e) => {
301
+ printMessage(JSON.stringify(e.payload), 'data');
302
+ });
303
+
304
+ setTimeout(() => {
305
+ tailLogs(source, levels, txid, logsObject.result.pagedResultsCookie);
306
+ }, 5000);
307
+ return null;
308
+ } catch (e) {
309
+ printMessage(`tail ERROR: tail data error - ${e}`, 'error');
310
+ return `tail ERROR: tail data error - ${e}`;
311
+ }
312
+ }
313
+
314
+ export async function provisionCreds() {
315
+ try {
316
+ let keyName = `frodo-${storage.session.getUsername()}`;
317
+ return getAPIKeys()
318
+ .then((response) => {
319
+ response.data.result.forEach((k) => {
320
+ if (k.name === keyName) {
321
+ // append current timestamp to name if the named key already exists
322
+ keyName = `${keyName}-${getCurrentTimestamp()}`;
323
+ }
324
+ });
325
+ return createAPIKeyAndSecret(keyName)
326
+ .then((resp) => {
327
+ if (resp.data.name !== keyName) {
328
+ printMessage(
329
+ `create keys ERROR: could not create log API key ${keyName}`,
330
+ 'error'
331
+ );
332
+ return null;
333
+ }
334
+ printMessage(
335
+ `Created a new log API key [${keyName}] in ${storage.session.getTenant()}`
336
+ );
337
+ return resp.data;
338
+ })
339
+ .catch((error) => {
340
+ printMessage(
341
+ `create keys ERROR: create keys call returned ${error}`,
342
+ 'error'
343
+ );
344
+ return null;
345
+ });
346
+ })
347
+ .catch((error) => {
348
+ printMessage(
349
+ `get keys ERROR: get keys call returned ${error}`,
350
+ 'error'
351
+ );
352
+ });
353
+ } catch (e) {
354
+ printMessage(`create keys ERROR: create keys data error - ${e}`, 'error');
355
+ return null;
356
+ }
357
+ }
@@ -0,0 +1,34 @@
1
+ import { getManagedObject } from '../api/ManagedObjectApi.js';
2
+
3
+ /**
4
+ * Resolve a managed object's uuid to a human readable username
5
+ * @param {String} type managed object type, e.g. teammember or alpha_user
6
+ * @param {String} id managed object _id
7
+ * @returns {String} resolved username or uuid if any error occurs during reslution
8
+ */
9
+ export async function resolveUserName(type, id) {
10
+ try {
11
+ return (await getManagedObject(type, id, ['userName'])).data.userName;
12
+ } catch (error) {
13
+ // eslint-disable-next-line no-empty
14
+ }
15
+ return id;
16
+ }
17
+
18
+ /**
19
+ * Resolve a managed object's uuid to a human readable full name
20
+ * @param {String} type managed object type, e.g. teammember or alpha_user
21
+ * @param {String} id managed object _id
22
+ * @returns {String} resolved full name or uuid if any error occurs during reslution
23
+ */
24
+ export async function resolveFullName(type, id) {
25
+ try {
26
+ const managedObject = (
27
+ await getManagedObject(type, id, ['givenName', 'sn'])
28
+ ).data;
29
+ return `${managedObject.givenName} ${managedObject.sn}`;
30
+ } catch (error) {
31
+ // eslint-disable-next-line no-empty
32
+ }
33
+ return id;
34
+ }
@@ -0,0 +1,151 @@
1
+ import fs from 'fs';
2
+ import { createTable, printMessage } from './utils/Console.js';
3
+ import {
4
+ getTypedFilename,
5
+ saveToFile,
6
+ titleCase,
7
+ validateImport,
8
+ } from './utils/ExportImportUtils.js';
9
+ import storage from '../storage/SessionStorage.js';
10
+ import {
11
+ getOAuth2Client,
12
+ getOAuth2Clients,
13
+ putOAuth2Client,
14
+ } from '../api/OAuth2ClientApi.js';
15
+ import { getOAuth2Provider } from '../api/OAuth2ProviderApi.js';
16
+ import { getRealmName } from '../api/utils/ApiUtils.js';
17
+
18
+ /**
19
+ * List OAuth2 clients
20
+ */
21
+ export async function listOAuth2Clients(long = false) {
22
+ try {
23
+ const clients = (await getOAuth2Clients()).data.result;
24
+ clients.sort((a, b) => a._id.localeCompare(b._id));
25
+ if (long) {
26
+ const table = createTable([
27
+ 'Client Id',
28
+ 'Status',
29
+ 'Client Type',
30
+ 'Grant Types',
31
+ 'Scopes',
32
+ 'Redirect URIs',
33
+ // 'Description',
34
+ ]);
35
+ const grantTypesMap = {
36
+ authorization_code: 'Authz Code',
37
+ client_credentials: 'Client Creds',
38
+ refresh_token: 'Refresh Token',
39
+ password: 'ROPC',
40
+ 'urn:ietf:params:oauth:grant-type:uma-ticket': 'UMA',
41
+ implicit: 'Implicit',
42
+ 'urn:ietf:params:oauth:grant-type:device_code': 'Device Code',
43
+ 'urn:ietf:params:oauth:grant-type:saml2-bearer': 'SAML2 Bearer',
44
+ 'urn:openid:params:grant-type:ciba': 'CIBA',
45
+ 'urn:ietf:params:oauth:grant-type:token-exchange': 'Token Exchange',
46
+ 'urn:ietf:params:oauth:grant-type:jwt-bearer': 'JWT Bearer',
47
+ };
48
+ clients.forEach((client) => {
49
+ table.push([
50
+ client._id,
51
+ client.coreOAuth2ClientConfig.status === 'Active'
52
+ ? 'Active'.brightGreen
53
+ : client.coreOAuth2ClientConfig.status.brightRed,
54
+ client.coreOAuth2ClientConfig.clientType,
55
+ client.advancedOAuth2ClientConfig.grantTypes
56
+ .map((type) => grantTypesMap[type])
57
+ .join('\n'),
58
+ client.coreOAuth2ClientConfig.scopes.join('\n'),
59
+ client.coreOAuth2ClientConfig.redirectionUris.join('\n'),
60
+ // wordwrap(client.description, 30),
61
+ ]);
62
+ });
63
+ printMessage(table.toString(), 'data');
64
+ } else {
65
+ clients.forEach((client) => {
66
+ printMessage(`${client._id}`, 'data');
67
+ });
68
+ }
69
+ } catch (error) {
70
+ printMessage(`Error listing scripts - ${error}`, 'error');
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Export OAuth2 client to file
76
+ * @param {String} id client id
77
+ * @param {String} file file name
78
+ */
79
+ export async function exportOAuth2ClientToFile(id, file) {
80
+ let fileName = getTypedFilename(id, 'oauth2.app');
81
+ if (file) {
82
+ fileName = file;
83
+ }
84
+ const oauth2Service = (await getOAuth2Provider()).data;
85
+ const client = (await getOAuth2Client(id)).data;
86
+ client._provider = oauth2Service;
87
+ saveToFile('application', [client], '_id', fileName);
88
+ }
89
+
90
+ /**
91
+ * Export all OAuth2 clients to file
92
+ * @param {String} file file name
93
+ */
94
+ export async function exportOAuth2ClientsToFile(file) {
95
+ let fileName = getTypedFilename(
96
+ `all${titleCase(getRealmName(storage.session.getRealm()))}Applications`,
97
+ 'oauth2.app'
98
+ );
99
+ if (file) {
100
+ fileName = file;
101
+ }
102
+ const oauth2Service = (await getOAuth2Provider()).data;
103
+ const clients = (await getOAuth2Clients()).data.result;
104
+ const exportData = [];
105
+ for (const client of clients) {
106
+ client._provider = oauth2Service;
107
+ exportData.push(client);
108
+ }
109
+ saveToFile('application', exportData, '_id', fileName);
110
+ }
111
+
112
+ /**
113
+ * Export all OAuth2 clients to separate files
114
+ */
115
+ export async function exportOAuth2ClientsToFiles() {
116
+ const oauth2Service = (await getOAuth2Provider()).data;
117
+ const clients = (await getOAuth2Clients()).data.result;
118
+ for (const client of clients) {
119
+ client._provider = oauth2Service;
120
+ const fileName = getTypedFilename(client._id, 'oauth2.app');
121
+ saveToFile('application', [client], '_id', fileName);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Import OAuth2 clients from file
127
+ * @param {String} file file name
128
+ */
129
+ export async function importOAuth2ClientsFromFile(file) {
130
+ fs.readFile(file, 'utf8', (err, data) => {
131
+ if (err) throw err;
132
+ const applicationData = JSON.parse(data);
133
+ if (validateImport(applicationData.meta)) {
134
+ for (const id in applicationData.application) {
135
+ if (
136
+ Object.prototype.hasOwnProperty.call(applicationData.application, id)
137
+ ) {
138
+ delete applicationData.application[id]._provider;
139
+ delete applicationData.application[id]._rev;
140
+ putOAuth2Client(id, applicationData.application[id]).then(
141
+ (result) => {
142
+ if (!result == null) printMessage(`Imported ${id}`);
143
+ }
144
+ );
145
+ }
146
+ }
147
+ } else {
148
+ printMessage('Import validation failed...', 'error');
149
+ }
150
+ });
151
+ }
@@ -0,0 +1,85 @@
1
+ import { queryAllManagedObjectsByType } from '../api/IdmConfigApi.js';
2
+ import storage from '../storage/SessionStorage.js';
3
+ import { printMessage } from './utils/Console.js';
4
+
5
+ /**
6
+ * Get organization managed object type
7
+ * @returns {String} organization managed object type in this realm
8
+ */
9
+ export function getRealmManagedOrganization() {
10
+ let realmManagedOrg = 'organization';
11
+ if (
12
+ storage.session.getDeploymentType() === global.CLOUD_DEPLOYMENT_TYPE_KEY
13
+ ) {
14
+ realmManagedOrg = `${storage.session.getRealm()}_organization`;
15
+ }
16
+ return realmManagedOrg;
17
+ }
18
+
19
+ /**
20
+ * Get organizations
21
+ * @returns {Promise} promise resolving to an object containing an array of organization objects
22
+ */
23
+ export async function getOrganizations() {
24
+ const orgs = [];
25
+ let result = {
26
+ result: [],
27
+ resultCount: 0,
28
+ pagedResultsCookie: null,
29
+ totalPagedResultsPolicy: 'NONE',
30
+ totalPagedResults: -1,
31
+ remainingPagedResults: -1,
32
+ };
33
+ try {
34
+ do {
35
+ // eslint-disable-next-line no-await-in-loop
36
+ result = await queryAllManagedObjectsByType(
37
+ getRealmManagedOrganization(),
38
+ ['name', 'parent/*/name', 'children/*/name'],
39
+ result.pagedResultsCookie
40
+ ).catch((queryAllManagedObjectsByTypeError) => {
41
+ printMessage(queryAllManagedObjectsByTypeError, 'error');
42
+ printMessage(
43
+ `Error querying ${getRealmManagedOrganization()} objects: ${queryAllManagedObjectsByTypeError}`,
44
+ 'error'
45
+ );
46
+ });
47
+ orgs.concat(result.result);
48
+ printMessage('.', 'text', false);
49
+ } while (result.pagedResultsCookie);
50
+ } catch (error) {
51
+ printMessage(error.response.data, 'error');
52
+ printMessage(`Error retrieving all organizations: ${error}`, 'error');
53
+ }
54
+ return orgs;
55
+ }
56
+
57
+ // unfinished work
58
+ export async function listOrganizationsTopDown() {
59
+ const orgs = [];
60
+ let result = {
61
+ result: [],
62
+ resultCount: 0,
63
+ pagedResultsCookie: null,
64
+ totalPagedResultsPolicy: 'NONE',
65
+ totalPagedResults: -1,
66
+ remainingPagedResults: -1,
67
+ };
68
+ do {
69
+ // eslint-disable-next-line no-await-in-loop
70
+ result = await queryAllManagedObjectsByType(
71
+ getRealmManagedOrganization(),
72
+ ['name', 'parent/*/name', 'children/*/name'],
73
+ result.pagedResultsCookie
74
+ ).catch((queryAllManagedObjectsByTypeError) => {
75
+ printMessage(queryAllManagedObjectsByTypeError, 'error');
76
+ printMessage(
77
+ `Error querying ${getRealmManagedOrganization()} objects: ${queryAllManagedObjectsByTypeError}`,
78
+ 'error'
79
+ );
80
+ });
81
+ orgs.concat(result.result);
82
+ printMessage('.', 'text', false);
83
+ } while (result.pagedResultsCookie);
84
+ return orgs;
85
+ }