@uipath/admin-tool 1.203.0-preview.205 → 1.204.0-preview.206
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/dist/index-0t7092fj.js +2375 -0
- package/dist/index-40a6zw4t.js +2246 -0
- package/dist/index-esy280as.js +634 -0
- package/dist/index-f7cp1ap1.js +2411 -0
- package/dist/index-m2vj29s2.js +6501 -0
- package/dist/index-xd0zd702.js +2740 -0
- package/dist/jmespath-v0jttxep.js +1485 -0
- package/dist/js-yaml-qv0npwpc.js +3153 -0
- package/dist/tool-016jy57n.js +1376 -0
- package/dist/tool-1yh8g550.js +52 -0
- package/dist/tool-687d8jq8.js +2531 -0
- package/dist/tool-mhb6cezm.js +2859 -0
- package/dist/tool-pmf56f99.js +1977 -0
- package/dist/tool-v13kh7v7.js +6601 -0
- package/dist/tool.js +70 -48739
- package/package.json +2 -2
|
@@ -0,0 +1,2375 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExternalApiResourceApi,
|
|
3
|
+
ExternalClientApi,
|
|
4
|
+
GroupApi,
|
|
5
|
+
ReferenceTokenApi,
|
|
6
|
+
RobotAccountApi,
|
|
7
|
+
SettingApi,
|
|
8
|
+
UserApi,
|
|
9
|
+
createApiClient,
|
|
10
|
+
createIdentityConfig
|
|
11
|
+
} from "./tool-mhb6cezm.js";
|
|
12
|
+
import"./tool-016jy57n.js";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_PAGE_SIZE,
|
|
15
|
+
Option,
|
|
16
|
+
OutputFormatter,
|
|
17
|
+
RESULTS,
|
|
18
|
+
catchError,
|
|
19
|
+
extractErrorMessage,
|
|
20
|
+
logger,
|
|
21
|
+
processContext,
|
|
22
|
+
splitScopeList
|
|
23
|
+
} from "./tool-v13kh7v7.js";
|
|
24
|
+
import"./tool-1yh8g550.js";
|
|
25
|
+
|
|
26
|
+
// ../identity-commands/src/commands/external-apps.ts
|
|
27
|
+
var INSTRUCTIONS = {
|
|
28
|
+
list: "Verify you are logged in. Run 'external-apps list --help' for options.",
|
|
29
|
+
get: "Verify the app ID exists. Use 'external-apps list' to find valid IDs.",
|
|
30
|
+
create: "Verify scopes are correct. Provide --user-scope and/or --app-scope.",
|
|
31
|
+
update: "Verify the app ID is correct.",
|
|
32
|
+
delete: "Verify the app ID exists. The app may have already been deleted.",
|
|
33
|
+
generateSecret: "Verify the app ID is correct.",
|
|
34
|
+
deleteSecret: "Verify the secret ID exists. Use 'external-apps get' to find valid secret IDs.",
|
|
35
|
+
fedCredList: "Verify the client ID is correct.",
|
|
36
|
+
fedCredGet: "Verify the credential ID exists. Use 'federated-credentials list' to find valid IDs.",
|
|
37
|
+
fedCredCreate: "Provide --name, --issuer, --audience, and --subject. These must match the external identity provider.",
|
|
38
|
+
fedCredUpdate: "Verify the credential ID exists and provide the updated fields.",
|
|
39
|
+
fedCredDelete: "Verify the credential ID exists. It may have already been deleted."
|
|
40
|
+
};
|
|
41
|
+
var EXTERNAL_APPS_LIST_EXAMPLES = [
|
|
42
|
+
{
|
|
43
|
+
Description: "List external apps in a partition",
|
|
44
|
+
Command: "uip admin external-apps list",
|
|
45
|
+
Output: {
|
|
46
|
+
Code: "ExternalClientList",
|
|
47
|
+
Data: [
|
|
48
|
+
{
|
|
49
|
+
id: "my-app",
|
|
50
|
+
name: "My Application",
|
|
51
|
+
isConfidential: true
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
var EXTERNAL_APPS_GET_EXAMPLES = [
|
|
58
|
+
{
|
|
59
|
+
Description: "Get external app details",
|
|
60
|
+
Command: "uip admin external-apps get my-app",
|
|
61
|
+
Output: {
|
|
62
|
+
Code: "ExternalClientDetails",
|
|
63
|
+
Data: {
|
|
64
|
+
id: "my-app",
|
|
65
|
+
name: "My Application",
|
|
66
|
+
isConfidential: true,
|
|
67
|
+
redirectUri: "https://example.com/callback"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
];
|
|
72
|
+
var EXTERNAL_APPS_CREATE_EXAMPLES = [
|
|
73
|
+
{
|
|
74
|
+
Description: "Create a confidential app with application scopes",
|
|
75
|
+
Command: 'uip admin external-apps create "My App" --app-scope "OR.Folders,OR.Assets"',
|
|
76
|
+
Output: {
|
|
77
|
+
Code: "ExternalClientCreated",
|
|
78
|
+
Data: {
|
|
79
|
+
id: "my-app",
|
|
80
|
+
name: "My App",
|
|
81
|
+
isConfidential: true,
|
|
82
|
+
secret: "generated-secret-value"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
Description: "Create a confidential app with both user and application scopes",
|
|
88
|
+
Command: 'uip admin external-apps create "My App" --user-scope "OR.Folders" --app-scope "OR.Jobs" --redirect-uri "https://example.com/callback"',
|
|
89
|
+
Output: {
|
|
90
|
+
Code: "ExternalClientCreated",
|
|
91
|
+
Data: {
|
|
92
|
+
id: "my-app",
|
|
93
|
+
name: "My App",
|
|
94
|
+
isConfidential: true,
|
|
95
|
+
secret: "generated-secret-value"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
Description: "Create a non-confidential app with user scopes",
|
|
101
|
+
Command: 'uip admin external-apps create "My SPA" --non-confidential --user-scope "OR.Folders" --redirect-uri "http://localhost:3000/callback"',
|
|
102
|
+
Output: {
|
|
103
|
+
Code: "ExternalClientCreated",
|
|
104
|
+
Data: {
|
|
105
|
+
id: "my-spa",
|
|
106
|
+
name: "My SPA",
|
|
107
|
+
isConfidential: false
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
var EXTERNAL_APPS_GENERATE_SECRET_EXAMPLES = [
|
|
113
|
+
{
|
|
114
|
+
Description: "Generate a new secret for an external app",
|
|
115
|
+
Command: "uip admin external-apps generate-secret my-app",
|
|
116
|
+
Output: {
|
|
117
|
+
Code: "ExternalClientSecretGenerated",
|
|
118
|
+
Data: {
|
|
119
|
+
id: 1,
|
|
120
|
+
secret: "generated-secret-value"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
var EXTERNAL_APPS_UPDATE_EXAMPLES = [
|
|
126
|
+
{
|
|
127
|
+
Description: "Update app name and scopes",
|
|
128
|
+
Command: 'uip admin external-apps update my-app-id --name "Updated App" --app-scope "OR.Folders"',
|
|
129
|
+
Output: {
|
|
130
|
+
Code: "ExternalClientUpdated"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
];
|
|
134
|
+
var EXTERNAL_APPS_DELETE_EXAMPLES = [
|
|
135
|
+
{
|
|
136
|
+
Description: "Delete an external app",
|
|
137
|
+
Command: "uip admin external-apps delete my-app-id",
|
|
138
|
+
Output: {
|
|
139
|
+
Code: "ExternalClientDeleted",
|
|
140
|
+
Data: { Id: "my-app-id", Status: "Deleted successfully" }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
var EXTERNAL_APPS_DELETE_SECRET_EXAMPLES = [
|
|
145
|
+
{
|
|
146
|
+
Description: "Delete a secret from an external app",
|
|
147
|
+
Command: "uip admin external-apps delete-secret 12345",
|
|
148
|
+
Output: {
|
|
149
|
+
Code: "ExternalClientSecretDeleted",
|
|
150
|
+
Data: { Id: "12345", Status: "Deleted successfully" }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
];
|
|
154
|
+
var FED_CRED_LIST_EXAMPLES = [
|
|
155
|
+
{
|
|
156
|
+
Description: "List federated credentials for an external app",
|
|
157
|
+
Command: "uip admin external-apps federated-credentials list my-app-id",
|
|
158
|
+
Output: {
|
|
159
|
+
Code: "FederatedCredentialList",
|
|
160
|
+
Data: [
|
|
161
|
+
{
|
|
162
|
+
id: "fc000000-0000-0000-0000-000000000001",
|
|
163
|
+
name: "GitHub Actions",
|
|
164
|
+
issuer: "https://token.actions.githubusercontent.com"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
var FED_CRED_GET_EXAMPLES = [
|
|
171
|
+
{
|
|
172
|
+
Description: "Get a federated credential by ID",
|
|
173
|
+
Command: "uip admin external-apps federated-credentials get my-app-id fc000000-0000-0000-0000-000000000001",
|
|
174
|
+
Output: {
|
|
175
|
+
Code: "FederatedCredentialDetails",
|
|
176
|
+
Data: {
|
|
177
|
+
id: "fc000000-0000-0000-0000-000000000001",
|
|
178
|
+
name: "GitHub Actions",
|
|
179
|
+
issuer: "https://token.actions.githubusercontent.com",
|
|
180
|
+
audience: "api://AzureADTokenExchange",
|
|
181
|
+
subject: "repo:org/repo:ref:refs/heads/main"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
];
|
|
186
|
+
var FED_CRED_CREATE_EXAMPLES = [
|
|
187
|
+
{
|
|
188
|
+
Description: "Create a federated credential for GitHub Actions OIDC",
|
|
189
|
+
Command: 'uip admin external-apps federated-credentials create my-app-id --name "GitHub Actions" --issuer "https://token.actions.githubusercontent.com" --audience "api://AzureADTokenExchange" --subject "repo:org/repo:ref:refs/heads/main"',
|
|
190
|
+
Output: {
|
|
191
|
+
Code: "FederatedCredentialCreated"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
];
|
|
195
|
+
var FED_CRED_UPDATE_EXAMPLES = [
|
|
196
|
+
{
|
|
197
|
+
Description: "Update a federated credential",
|
|
198
|
+
Command: 'uip admin external-apps federated-credentials update my-app-id fc000000-0000-0000-0000-000000000001 --name "GitHub Actions (prod)" --issuer "https://token.actions.githubusercontent.com" --audience "api://AzureADTokenExchange" --subject "repo:org/repo:environment:production"',
|
|
199
|
+
Output: {
|
|
200
|
+
Code: "FederatedCredentialUpdated"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
var FED_CRED_DELETE_EXAMPLES = [
|
|
205
|
+
{
|
|
206
|
+
Description: "Delete a federated credential",
|
|
207
|
+
Command: "uip admin external-apps federated-credentials delete my-app-id fc000000-0000-0000-0000-000000000001",
|
|
208
|
+
Output: {
|
|
209
|
+
Code: "FederatedCredentialDeleted",
|
|
210
|
+
Data: {
|
|
211
|
+
Id: "fc000000-0000-0000-0000-000000000001",
|
|
212
|
+
Status: "Deleted successfully"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
];
|
|
217
|
+
function parseScopes(list, type) {
|
|
218
|
+
return splitScopeList(list).map((name) => ({ name, type }));
|
|
219
|
+
}
|
|
220
|
+
function foldDeprecatedScopeAlias(options) {
|
|
221
|
+
if (options.scope !== undefined && options.appScope === undefined) {
|
|
222
|
+
options.appScope = options.scope;
|
|
223
|
+
return "--scope";
|
|
224
|
+
}
|
|
225
|
+
return "--app-scope";
|
|
226
|
+
}
|
|
227
|
+
function findBlankScopeOption(options, appScopeOption) {
|
|
228
|
+
if (options.userScope !== undefined && splitScopeList(options.userScope).length === 0) {
|
|
229
|
+
return "--user-scope";
|
|
230
|
+
}
|
|
231
|
+
if (options.appScope !== undefined && splitScopeList(options.appScope).length === 0) {
|
|
232
|
+
return appScopeOption;
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
function reportBlankScopeOption(option) {
|
|
237
|
+
OutputFormatter.error({
|
|
238
|
+
Result: RESULTS.Failure,
|
|
239
|
+
Message: `No scope names found in ${option}.`,
|
|
240
|
+
Instructions: `Pass one or more scopes, comma- or space-separated (e.g. ${option} "OR.Folders,OR.Assets").`
|
|
241
|
+
});
|
|
242
|
+
processContext.exit(1);
|
|
243
|
+
}
|
|
244
|
+
function warnDeprecatedOrganization(org) {
|
|
245
|
+
if (org !== undefined) {
|
|
246
|
+
logger.warn("--organization is deprecated and ignored; organization is derived from login context");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
var registerExternalAppsCommand = (program) => {
|
|
250
|
+
const externalApps = program.command("external-apps").description("Manage external OAuth2 clients. List, get, create, update, delete clients and manage secrets.");
|
|
251
|
+
externalApps.command("list").description("List external apps in a partition.").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
252
|
+
warnDeprecatedOrganization(options.organization);
|
|
253
|
+
const [error, result] = await catchError((async () => {
|
|
254
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
255
|
+
loginValidity: options.loginValidity,
|
|
256
|
+
organization: options.organization
|
|
257
|
+
});
|
|
258
|
+
const orgId = organizationId;
|
|
259
|
+
return await api.externalClientGet({
|
|
260
|
+
partitionGlobalId: orgId
|
|
261
|
+
});
|
|
262
|
+
})());
|
|
263
|
+
if (error) {
|
|
264
|
+
OutputFormatter.error({
|
|
265
|
+
Result: RESULTS.Failure,
|
|
266
|
+
Message: await extractErrorMessage(error),
|
|
267
|
+
Instructions: INSTRUCTIONS.list
|
|
268
|
+
});
|
|
269
|
+
processContext.exit(1);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
OutputFormatter.success({
|
|
273
|
+
Result: RESULTS.Success,
|
|
274
|
+
Code: "ExternalClientList",
|
|
275
|
+
Data: result ?? []
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
externalApps.command("get").description("Get external app details. " + "Use 'external-apps list' to retrieve the client ID.").argument("<client-id>", "External app ID").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_GET_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
279
|
+
warnDeprecatedOrganization(options.organization);
|
|
280
|
+
const [error, client] = await catchError((async () => {
|
|
281
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
282
|
+
loginValidity: options.loginValidity,
|
|
283
|
+
organization: options.organization
|
|
284
|
+
});
|
|
285
|
+
const orgId = organizationId;
|
|
286
|
+
return await api.externalClientGetWithClientId({
|
|
287
|
+
partitionGlobalId: orgId,
|
|
288
|
+
clientId
|
|
289
|
+
});
|
|
290
|
+
})());
|
|
291
|
+
if (error) {
|
|
292
|
+
OutputFormatter.error({
|
|
293
|
+
Result: RESULTS.Failure,
|
|
294
|
+
Message: await extractErrorMessage(error),
|
|
295
|
+
Instructions: INSTRUCTIONS.get
|
|
296
|
+
});
|
|
297
|
+
processContext.exit(1);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
OutputFormatter.success({
|
|
301
|
+
Result: RESULTS.Success,
|
|
302
|
+
Code: "ExternalClientDetails",
|
|
303
|
+
Data: client
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
externalApps.command("create").description("Create an external app (confidential by default). " + "Use --non-confidential for public clients (SPAs, mobile apps).").argument("<name>", "App display name").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--redirect-uri <uri>", "Redirect URI(s) for OAuth2 flow (comma-separated for multiple)").option("--user-scope <scopes>", "User (delegated) scopes, comma- or space-separated").option("--app-scope <scopes>", "Application (app-only) scopes, comma- or space-separated").option("--scope <scopes>", "Alias for --app-scope (deprecated)").option("--non-confidential", "Create a non-confidential (public) app — no client secret, requires redirect URI").option("--no-secret", "Skip generating a client secret on creation").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_CREATE_EXAMPLES).trackedAction(processContext, async (name, options) => {
|
|
307
|
+
warnDeprecatedOrganization(options.organization);
|
|
308
|
+
const appScopeOption = foldDeprecatedScopeAlias(options);
|
|
309
|
+
const blankOption = findBlankScopeOption(options, appScopeOption);
|
|
310
|
+
if (blankOption) {
|
|
311
|
+
reportBlankScopeOption(blankOption);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (!options.userScope && !options.appScope) {
|
|
315
|
+
OutputFormatter.error({
|
|
316
|
+
Result: RESULTS.Failure,
|
|
317
|
+
Message: "At least one of --user-scope or --app-scope is required.",
|
|
318
|
+
Instructions: "Use --user-scope for delegated permissions and/or --app-scope for app-only permissions."
|
|
319
|
+
});
|
|
320
|
+
processContext.exit(1);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const isConfidential = !options.nonConfidential;
|
|
324
|
+
const hasUserScopes = !!options.userScope;
|
|
325
|
+
if (!isConfidential && options.appScope) {
|
|
326
|
+
OutputFormatter.error({
|
|
327
|
+
Result: RESULTS.Failure,
|
|
328
|
+
Message: "Non-confidential apps only support user scopes. Use --user-scope instead of --app-scope.",
|
|
329
|
+
Instructions: "Remove --app-scope and use --user-scope for delegated permissions."
|
|
330
|
+
});
|
|
331
|
+
processContext.exit(1);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (!options.redirectUri && (!isConfidential || hasUserScopes)) {
|
|
335
|
+
const reason = !isConfidential ? "Non-confidential apps require" : "User scopes require";
|
|
336
|
+
OutputFormatter.error({
|
|
337
|
+
Result: RESULTS.Failure,
|
|
338
|
+
Message: `${reason} --redirect-uri for the OAuth2 authorization code flow.`,
|
|
339
|
+
Instructions: "Provide --redirect-uri (e.g. http://localhost:3000/callback)."
|
|
340
|
+
});
|
|
341
|
+
processContext.exit(1);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const scopes = [
|
|
345
|
+
...parseScopes(options.userScope, "user"),
|
|
346
|
+
...parseScopes(options.appScope, "application")
|
|
347
|
+
];
|
|
348
|
+
const generateFirstSecret = isConfidential && options.secret !== false;
|
|
349
|
+
const [error, result] = await catchError((async () => {
|
|
350
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
351
|
+
loginValidity: options.loginValidity,
|
|
352
|
+
organization: options.organization
|
|
353
|
+
});
|
|
354
|
+
const orgId = organizationId;
|
|
355
|
+
const createCommand = {
|
|
356
|
+
partitionGlobalId: orgId,
|
|
357
|
+
name,
|
|
358
|
+
isConfidential,
|
|
359
|
+
scopes,
|
|
360
|
+
redirectUri: options.redirectUri,
|
|
361
|
+
generateFirstSecret
|
|
362
|
+
};
|
|
363
|
+
return await api.externalClientCreate({
|
|
364
|
+
createExternalClientCommand: createCommand
|
|
365
|
+
});
|
|
366
|
+
})());
|
|
367
|
+
if (error) {
|
|
368
|
+
OutputFormatter.error({
|
|
369
|
+
Result: RESULTS.Failure,
|
|
370
|
+
Message: await extractErrorMessage(error),
|
|
371
|
+
Instructions: INSTRUCTIONS.create
|
|
372
|
+
});
|
|
373
|
+
processContext.exit(1);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
OutputFormatter.success({
|
|
377
|
+
Result: RESULTS.Success,
|
|
378
|
+
Code: "ExternalClientCreated",
|
|
379
|
+
Data: result
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
externalApps.command("update").description("Update an external app. Use 'external-apps list' to retrieve the client ID.").argument("<client-id>", "External app ID").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("-n, --name <name>", "New display name").option("--redirect-uri <uri>", "Redirect URI(s) for OAuth2 flow (comma-separated for multiple)").option("--user-scope <scopes>", "User (delegated) scopes, comma- or space-separated").option("--app-scope <scopes>", "Application (app-only) scopes, comma- or space-separated").option("--scope <scopes>", "Alias for --app-scope (deprecated)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_UPDATE_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
383
|
+
warnDeprecatedOrganization(options.organization);
|
|
384
|
+
const appScopeOption = foldDeprecatedScopeAlias(options);
|
|
385
|
+
const hasScopes = options.userScope !== undefined || options.appScope !== undefined;
|
|
386
|
+
if (options.name === undefined && options.redirectUri === undefined && !hasScopes) {
|
|
387
|
+
OutputFormatter.error({
|
|
388
|
+
Result: RESULTS.Failure,
|
|
389
|
+
Message: "No fields to update",
|
|
390
|
+
Instructions: "Provide at least one of --name, --redirect-uri, --user-scope, or --app-scope."
|
|
391
|
+
});
|
|
392
|
+
processContext.exit(1);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const blankOption = findBlankScopeOption(options, appScopeOption);
|
|
396
|
+
if (blankOption) {
|
|
397
|
+
reportBlankScopeOption(blankOption);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const [fetchError, fetchResult] = await catchError((async () => {
|
|
401
|
+
const { api: api2, organizationId } = await createApiClient(ExternalClientApi, {
|
|
402
|
+
loginValidity: options.loginValidity,
|
|
403
|
+
organization: options.organization
|
|
404
|
+
});
|
|
405
|
+
const orgId2 = organizationId;
|
|
406
|
+
const current2 = await api2.externalClientGetWithClientId({
|
|
407
|
+
partitionGlobalId: orgId2,
|
|
408
|
+
clientId
|
|
409
|
+
});
|
|
410
|
+
return { api: api2, orgId: orgId2, current: current2 };
|
|
411
|
+
})());
|
|
412
|
+
if (fetchError) {
|
|
413
|
+
OutputFormatter.error({
|
|
414
|
+
Result: RESULTS.Failure,
|
|
415
|
+
Message: await extractErrorMessage(fetchError),
|
|
416
|
+
Instructions: INSTRUCTIONS.update
|
|
417
|
+
});
|
|
418
|
+
processContext.exit(1);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const { api, orgId, current } = fetchResult;
|
|
422
|
+
if (!current.isConfidential && options.appScope) {
|
|
423
|
+
OutputFormatter.error({
|
|
424
|
+
Result: RESULTS.Failure,
|
|
425
|
+
Message: "Non-confidential apps only support user scopes. Use --user-scope instead of --app-scope.",
|
|
426
|
+
Instructions: "Remove --app-scope and use --user-scope for delegated permissions."
|
|
427
|
+
});
|
|
428
|
+
processContext.exit(1);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const hasUserScopes = options.userScope !== undefined;
|
|
432
|
+
const effectiveRedirectUri = options.redirectUri ?? current.redirectUri;
|
|
433
|
+
if (hasUserScopes && !effectiveRedirectUri) {
|
|
434
|
+
OutputFormatter.error({
|
|
435
|
+
Result: RESULTS.Failure,
|
|
436
|
+
Message: "User scopes require a redirect URI. Provide --redirect-uri or set one first.",
|
|
437
|
+
Instructions: "Provide --redirect-uri (e.g. http://localhost:3000/callback)."
|
|
438
|
+
});
|
|
439
|
+
processContext.exit(1);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const scopes = hasScopes ? [
|
|
443
|
+
...parseScopes(options.userScope, "user"),
|
|
444
|
+
...parseScopes(options.appScope, "application")
|
|
445
|
+
] : current.resources?.flatMap((r) => r.scopes ?? []) ?? [];
|
|
446
|
+
const updateCommand = {
|
|
447
|
+
scopes
|
|
448
|
+
};
|
|
449
|
+
if (options.name !== undefined)
|
|
450
|
+
updateCommand.name = options.name;
|
|
451
|
+
if (options.redirectUri !== undefined)
|
|
452
|
+
updateCommand.redirectUri = options.redirectUri;
|
|
453
|
+
const [error, result] = await catchError(api.externalClientUpdate({
|
|
454
|
+
partitionGlobalId: orgId,
|
|
455
|
+
clientId,
|
|
456
|
+
updateExternalClientCommand: updateCommand
|
|
457
|
+
}));
|
|
458
|
+
if (error) {
|
|
459
|
+
OutputFormatter.error({
|
|
460
|
+
Result: RESULTS.Failure,
|
|
461
|
+
Message: await extractErrorMessage(error),
|
|
462
|
+
Instructions: INSTRUCTIONS.update
|
|
463
|
+
});
|
|
464
|
+
processContext.exit(1);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
OutputFormatter.success({
|
|
468
|
+
Result: RESULTS.Success,
|
|
469
|
+
Code: "ExternalClientUpdated",
|
|
470
|
+
Data: result
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
externalApps.command("delete").description("Delete an external app. " + "Use 'external-apps list' to retrieve the client ID.").argument("<client-id>", "External app ID").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_DELETE_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
474
|
+
warnDeprecatedOrganization(options.organization);
|
|
475
|
+
const [error] = await catchError((async () => {
|
|
476
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
477
|
+
loginValidity: options.loginValidity,
|
|
478
|
+
organization: options.organization
|
|
479
|
+
});
|
|
480
|
+
const orgId = organizationId;
|
|
481
|
+
return await api.externalClientDelete({
|
|
482
|
+
partitionGlobalId: orgId,
|
|
483
|
+
clientId
|
|
484
|
+
});
|
|
485
|
+
})());
|
|
486
|
+
if (error) {
|
|
487
|
+
OutputFormatter.error({
|
|
488
|
+
Result: RESULTS.Failure,
|
|
489
|
+
Message: await extractErrorMessage(error),
|
|
490
|
+
Instructions: INSTRUCTIONS.delete
|
|
491
|
+
});
|
|
492
|
+
processContext.exit(1);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
OutputFormatter.success({
|
|
496
|
+
Result: RESULTS.Success,
|
|
497
|
+
Code: "ExternalClientDeleted",
|
|
498
|
+
Data: {
|
|
499
|
+
Id: clientId,
|
|
500
|
+
Status: "Deleted successfully"
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
externalApps.command("generate-secret").description("Generate a new secret for an external app. " + "Returns the secret value (only shown once).").argument("<client-id>", "External app ID").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--description <text>", "Description for the secret").option("--expiration <date>", "Expiration date (ISO 8601)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_GENERATE_SECRET_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
505
|
+
warnDeprecatedOrganization(options.organization);
|
|
506
|
+
const [error, result] = await catchError((async () => {
|
|
507
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
508
|
+
loginValidity: options.loginValidity,
|
|
509
|
+
organization: options.organization
|
|
510
|
+
});
|
|
511
|
+
const orgId = organizationId;
|
|
512
|
+
return await api.externalClientGenerateSecret({
|
|
513
|
+
createClientSecretCommand: {
|
|
514
|
+
clientId,
|
|
515
|
+
partitionGlobalId: orgId,
|
|
516
|
+
description: options.description,
|
|
517
|
+
expiryTime: options.expiration ? new Date(options.expiration) : undefined
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
})());
|
|
521
|
+
if (error) {
|
|
522
|
+
OutputFormatter.error({
|
|
523
|
+
Result: RESULTS.Failure,
|
|
524
|
+
Message: await extractErrorMessage(error),
|
|
525
|
+
Instructions: INSTRUCTIONS.generateSecret
|
|
526
|
+
});
|
|
527
|
+
processContext.exit(1);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
OutputFormatter.success({
|
|
531
|
+
Result: RESULTS.Success,
|
|
532
|
+
Code: "ExternalClientSecretGenerated",
|
|
533
|
+
Data: result
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
externalApps.command("delete-secret").description("Delete a specific secret from an external app.").argument("<secret-id>", "Secret ID (numeric)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(EXTERNAL_APPS_DELETE_SECRET_EXAMPLES).trackedAction(processContext, async (secretId, options) => {
|
|
537
|
+
warnDeprecatedOrganization(options.organization);
|
|
538
|
+
const [error] = await catchError((async () => {
|
|
539
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
540
|
+
loginValidity: options.loginValidity,
|
|
541
|
+
organization: options.organization
|
|
542
|
+
});
|
|
543
|
+
const orgId = organizationId;
|
|
544
|
+
return await api.externalClientDeleteSecret({
|
|
545
|
+
partitionGlobalId: orgId,
|
|
546
|
+
secretId: Number.parseInt(secretId, 10)
|
|
547
|
+
});
|
|
548
|
+
})());
|
|
549
|
+
if (error) {
|
|
550
|
+
OutputFormatter.error({
|
|
551
|
+
Result: RESULTS.Failure,
|
|
552
|
+
Message: await extractErrorMessage(error),
|
|
553
|
+
Instructions: INSTRUCTIONS.deleteSecret
|
|
554
|
+
});
|
|
555
|
+
processContext.exit(1);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
OutputFormatter.success({
|
|
559
|
+
Result: RESULTS.Success,
|
|
560
|
+
Code: "ExternalClientSecretDeleted",
|
|
561
|
+
Data: {
|
|
562
|
+
Id: secretId,
|
|
563
|
+
Status: "Deleted successfully"
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
const fedCreds = externalApps.command("federated-credentials").description("Manage federated credentials for external apps. " + "Enable workload identity federation with external identity providers (e.g. GitHub Actions, Azure AD).");
|
|
568
|
+
fedCreds.command("list").description("List federated credentials for an external app.").argument("<client-id>", "External app ID").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(FED_CRED_LIST_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
569
|
+
warnDeprecatedOrganization(options.organization);
|
|
570
|
+
const [error, result] = await catchError((async () => {
|
|
571
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
572
|
+
loginValidity: options.loginValidity,
|
|
573
|
+
organization: options.organization
|
|
574
|
+
});
|
|
575
|
+
const orgId = organizationId;
|
|
576
|
+
return await api.externalClientGetFederatedCredentials({
|
|
577
|
+
partitionGlobalId: orgId,
|
|
578
|
+
clientId
|
|
579
|
+
});
|
|
580
|
+
})());
|
|
581
|
+
if (error) {
|
|
582
|
+
OutputFormatter.error({
|
|
583
|
+
Result: RESULTS.Failure,
|
|
584
|
+
Message: await extractErrorMessage(error),
|
|
585
|
+
Instructions: INSTRUCTIONS.fedCredList
|
|
586
|
+
});
|
|
587
|
+
processContext.exit(1);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
OutputFormatter.success({
|
|
591
|
+
Result: RESULTS.Success,
|
|
592
|
+
Code: "FederatedCredentialList",
|
|
593
|
+
Data: result ?? []
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
fedCreds.command("get").description("Get a federated credential by ID.").argument("<client-id>", "External app ID").argument("<credential-id>", "Federated credential ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(FED_CRED_GET_EXAMPLES).trackedAction(processContext, async (clientId, credentialId, options) => {
|
|
597
|
+
warnDeprecatedOrganization(options.organization);
|
|
598
|
+
const [error, result] = await catchError((async () => {
|
|
599
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
600
|
+
loginValidity: options.loginValidity,
|
|
601
|
+
organization: options.organization
|
|
602
|
+
});
|
|
603
|
+
const orgId = organizationId;
|
|
604
|
+
return await api.externalClientGetFederatedCredentialByIdByCredentialId({
|
|
605
|
+
partitionGlobalId: orgId,
|
|
606
|
+
clientId,
|
|
607
|
+
credentialId
|
|
608
|
+
});
|
|
609
|
+
})());
|
|
610
|
+
if (error) {
|
|
611
|
+
OutputFormatter.error({
|
|
612
|
+
Result: RESULTS.Failure,
|
|
613
|
+
Message: await extractErrorMessage(error),
|
|
614
|
+
Instructions: INSTRUCTIONS.fedCredGet
|
|
615
|
+
});
|
|
616
|
+
processContext.exit(1);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
OutputFormatter.success({
|
|
620
|
+
Result: RESULTS.Success,
|
|
621
|
+
Code: "FederatedCredentialDetails",
|
|
622
|
+
Data: result
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
fedCreds.command("create").description("Create a federated credential for an external app. " + "Maps an external identity (e.g. GitHub Actions OIDC) to this app.").argument("<client-id>", "External app ID").requiredOption("-n, --name <name>", "Credential name").requiredOption("--issuer <url>", "Token issuer URL (e.g. https://token.actions.githubusercontent.com)").requiredOption("--audience <audience>", "Expected audience claim (e.g. api://AzureADTokenExchange)").requiredOption("--subject <subject>", "Expected subject claim (e.g. repo:org/repo:ref:refs/heads/main)").option("--description <text>", "Description for the credential").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(FED_CRED_CREATE_EXAMPLES).trackedAction(processContext, async (clientId, options) => {
|
|
626
|
+
warnDeprecatedOrganization(options.organization);
|
|
627
|
+
const [error, result] = await catchError((async () => {
|
|
628
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
629
|
+
loginValidity: options.loginValidity,
|
|
630
|
+
organization: options.organization
|
|
631
|
+
});
|
|
632
|
+
const orgId = organizationId;
|
|
633
|
+
const cmd = {
|
|
634
|
+
name: options.name,
|
|
635
|
+
issuer: options.issuer,
|
|
636
|
+
audience: options.audience,
|
|
637
|
+
subject: options.subject,
|
|
638
|
+
description: options.description
|
|
639
|
+
};
|
|
640
|
+
return await api.externalClientCreateFederatedCredential({
|
|
641
|
+
partitionGlobalId: orgId,
|
|
642
|
+
clientId,
|
|
643
|
+
createFederatedCredentialCommand: cmd
|
|
644
|
+
});
|
|
645
|
+
})());
|
|
646
|
+
if (error) {
|
|
647
|
+
OutputFormatter.error({
|
|
648
|
+
Result: RESULTS.Failure,
|
|
649
|
+
Message: await extractErrorMessage(error),
|
|
650
|
+
Instructions: INSTRUCTIONS.fedCredCreate
|
|
651
|
+
});
|
|
652
|
+
processContext.exit(1);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
OutputFormatter.success({
|
|
656
|
+
Result: RESULTS.Success,
|
|
657
|
+
Code: "FederatedCredentialCreated",
|
|
658
|
+
Data: result
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
fedCreds.command("update").description("Update a federated credential.").argument("<client-id>", "External app ID").argument("<credential-id>", "Federated credential ID (UUID)").requiredOption("-n, --name <name>", "Credential name").requiredOption("--issuer <url>", "Token issuer URL").requiredOption("--audience <audience>", "Expected audience claim").requiredOption("--subject <subject>", "Expected subject claim").option("--description <text>", "Description for the credential").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(FED_CRED_UPDATE_EXAMPLES).trackedAction(processContext, async (clientId, credentialId, options) => {
|
|
662
|
+
warnDeprecatedOrganization(options.organization);
|
|
663
|
+
const [error, result] = await catchError((async () => {
|
|
664
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
665
|
+
loginValidity: options.loginValidity,
|
|
666
|
+
organization: options.organization
|
|
667
|
+
});
|
|
668
|
+
const orgId = organizationId;
|
|
669
|
+
const cmd = {
|
|
670
|
+
name: options.name,
|
|
671
|
+
issuer: options.issuer,
|
|
672
|
+
audience: options.audience,
|
|
673
|
+
subject: options.subject,
|
|
674
|
+
description: options.description
|
|
675
|
+
};
|
|
676
|
+
return await api.externalClientUpdateFederatedCredential({
|
|
677
|
+
partitionGlobalId: orgId,
|
|
678
|
+
clientId,
|
|
679
|
+
credentialId,
|
|
680
|
+
updateFederatedCredentialCommand: cmd
|
|
681
|
+
});
|
|
682
|
+
})());
|
|
683
|
+
if (error) {
|
|
684
|
+
OutputFormatter.error({
|
|
685
|
+
Result: RESULTS.Failure,
|
|
686
|
+
Message: await extractErrorMessage(error),
|
|
687
|
+
Instructions: INSTRUCTIONS.fedCredUpdate
|
|
688
|
+
});
|
|
689
|
+
processContext.exit(1);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
OutputFormatter.success({
|
|
693
|
+
Result: RESULTS.Success,
|
|
694
|
+
Code: "FederatedCredentialUpdated",
|
|
695
|
+
Data: result
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
fedCreds.command("delete").description("Delete a federated credential from an external app.").argument("<client-id>", "External app ID").argument("<credential-id>", "Federated credential ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(FED_CRED_DELETE_EXAMPLES).trackedAction(processContext, async (clientId, credentialId, options) => {
|
|
699
|
+
warnDeprecatedOrganization(options.organization);
|
|
700
|
+
const [error] = await catchError((async () => {
|
|
701
|
+
const { api, organizationId } = await createApiClient(ExternalClientApi, {
|
|
702
|
+
loginValidity: options.loginValidity,
|
|
703
|
+
organization: options.organization
|
|
704
|
+
});
|
|
705
|
+
const orgId = organizationId;
|
|
706
|
+
return await api.externalClientDeleteFederatedCredential({
|
|
707
|
+
partitionGlobalId: orgId,
|
|
708
|
+
clientId,
|
|
709
|
+
credentialId
|
|
710
|
+
});
|
|
711
|
+
})());
|
|
712
|
+
if (error) {
|
|
713
|
+
OutputFormatter.error({
|
|
714
|
+
Result: RESULTS.Failure,
|
|
715
|
+
Message: await extractErrorMessage(error),
|
|
716
|
+
Instructions: INSTRUCTIONS.fedCredDelete
|
|
717
|
+
});
|
|
718
|
+
processContext.exit(1);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
OutputFormatter.success({
|
|
722
|
+
Result: RESULTS.Success,
|
|
723
|
+
Code: "FederatedCredentialDeleted",
|
|
724
|
+
Data: {
|
|
725
|
+
Id: credentialId,
|
|
726
|
+
Status: "Deleted successfully"
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// ../identity-commands/src/commands/groups.ts
|
|
733
|
+
var INSTRUCTIONS2 = {
|
|
734
|
+
list: "Verify you are logged in. Run 'groups list --help' for options.",
|
|
735
|
+
get: "Verify the group ID exists. Use 'groups list' to find valid IDs.",
|
|
736
|
+
create: "Verify the group name is correct. Group names must be unique within a partition.",
|
|
737
|
+
update: "Verify the group ID is correct. Use 'groups list' to find valid IDs.",
|
|
738
|
+
delete: "Verify the group ID exists. The group may have already been deleted.",
|
|
739
|
+
listMembers: "Verify the group ID is correct.",
|
|
740
|
+
addMembers: "Verify the group ID and user IDs are correct. Use 'users list' to find valid user IDs.",
|
|
741
|
+
revokeMembers: "Verify the user IDs are current members of the group."
|
|
742
|
+
};
|
|
743
|
+
var GROUPS_LIST_EXAMPLES = [
|
|
744
|
+
{
|
|
745
|
+
Description: "List groups in a partition",
|
|
746
|
+
Command: "uip admin groups list",
|
|
747
|
+
Output: {
|
|
748
|
+
Code: "GroupList",
|
|
749
|
+
Data: [
|
|
750
|
+
{
|
|
751
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
752
|
+
name: "Administrators",
|
|
753
|
+
displayName: "Administrators",
|
|
754
|
+
type: "BuiltIn",
|
|
755
|
+
creationTime: "2026-01-15T10:00:00.000Z"
|
|
756
|
+
}
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
];
|
|
761
|
+
var GROUPS_GET_EXAMPLES = [
|
|
762
|
+
{
|
|
763
|
+
Description: "Get group details",
|
|
764
|
+
Command: "uip admin groups get a1b2c3d4-0000-0000-0000-000000000001",
|
|
765
|
+
Output: {
|
|
766
|
+
Code: "GroupDetails",
|
|
767
|
+
Data: {
|
|
768
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
769
|
+
name: "Administrators",
|
|
770
|
+
displayName: "Administrators",
|
|
771
|
+
type: "BuiltIn",
|
|
772
|
+
creationTime: "2026-01-15T10:00:00.000Z"
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
];
|
|
777
|
+
var GROUPS_CREATE_EXAMPLES = [
|
|
778
|
+
{
|
|
779
|
+
Description: "Create a new group",
|
|
780
|
+
Command: 'uip admin groups create "DevOps Team"',
|
|
781
|
+
Output: {
|
|
782
|
+
Code: "GroupCreated",
|
|
783
|
+
Data: {
|
|
784
|
+
id: "a1b2c3d4-0000-0000-0000-000000000002",
|
|
785
|
+
name: "DevOps Team",
|
|
786
|
+
displayName: "DevOps Team",
|
|
787
|
+
type: "Custom"
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
];
|
|
792
|
+
var GROUPS_UPDATE_EXAMPLES = [
|
|
793
|
+
{
|
|
794
|
+
Description: "Rename a group",
|
|
795
|
+
Command: 'uip admin groups update a1b2c3d4-0000-0000-0000-000000000001 --name "Renamed Group"',
|
|
796
|
+
Output: {
|
|
797
|
+
Code: "GroupUpdated"
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
];
|
|
801
|
+
var GROUPS_DELETE_EXAMPLES = [
|
|
802
|
+
{
|
|
803
|
+
Description: "Delete a group",
|
|
804
|
+
Command: "uip admin groups delete a1b2c3d4-0000-0000-0000-000000000002",
|
|
805
|
+
Output: {
|
|
806
|
+
Code: "GroupDeleted",
|
|
807
|
+
Data: {
|
|
808
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000002",
|
|
809
|
+
Status: "Deleted successfully"
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
];
|
|
814
|
+
var MEMBERS_LIST_EXAMPLES = [
|
|
815
|
+
{
|
|
816
|
+
Description: "List members of a group",
|
|
817
|
+
Command: "uip admin groups members list a1b2c3d4-0000-0000-0000-000000000001",
|
|
818
|
+
Output: {
|
|
819
|
+
Code: "GroupMembers"
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
];
|
|
823
|
+
var MEMBERS_ADD_EXAMPLES = [
|
|
824
|
+
{
|
|
825
|
+
Description: "Add users to a group",
|
|
826
|
+
Command: 'uip admin groups members add a1b2c3d4-0000-0000-0000-000000000001 --user-ids "user-id-1,user-id-2"',
|
|
827
|
+
Output: {
|
|
828
|
+
Code: "GroupMembersAdded"
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
];
|
|
832
|
+
var MEMBERS_REVOKE_EXAMPLES = [
|
|
833
|
+
{
|
|
834
|
+
Description: "Revoke users from a group",
|
|
835
|
+
Command: 'uip admin groups members revoke a1b2c3d4-0000-0000-0000-000000000001 --user-ids "user-id-1"',
|
|
836
|
+
Output: {
|
|
837
|
+
Code: "GroupMembersRevoked"
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
];
|
|
841
|
+
function warnDeprecatedOrganization2(org) {
|
|
842
|
+
if (org !== undefined) {
|
|
843
|
+
logger.warn("--organization is deprecated and ignored; organization is derived from login context");
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
var registerGroupsCommand = (program) => {
|
|
847
|
+
const groups = program.command("groups").description("Manage groups. List, get, create, update, and delete groups within a partition.");
|
|
848
|
+
groups.command("list").description("List all groups in a partition. " + "Returns group ID, name, type, and creation time.").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(GROUPS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
849
|
+
warnDeprecatedOrganization2(options.organization);
|
|
850
|
+
const [error, result] = await catchError((async () => {
|
|
851
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
852
|
+
loginValidity: options.loginValidity
|
|
853
|
+
});
|
|
854
|
+
const orgId = organizationId;
|
|
855
|
+
return await api.groupGetGroups({
|
|
856
|
+
partitionGlobalId: orgId
|
|
857
|
+
});
|
|
858
|
+
})());
|
|
859
|
+
if (error) {
|
|
860
|
+
OutputFormatter.error({
|
|
861
|
+
Result: RESULTS.Failure,
|
|
862
|
+
Message: await extractErrorMessage(error),
|
|
863
|
+
Instructions: INSTRUCTIONS2.list
|
|
864
|
+
});
|
|
865
|
+
processContext.exit(1);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
OutputFormatter.success({
|
|
869
|
+
Result: RESULTS.Success,
|
|
870
|
+
Code: "GroupList",
|
|
871
|
+
Data: result ?? []
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
groups.command("get").description("Get group details by ID. " + "Use 'groups list' to retrieve the group ID.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(GROUPS_GET_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
875
|
+
warnDeprecatedOrganization2(options.organization);
|
|
876
|
+
const [error, group] = await catchError((async () => {
|
|
877
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
878
|
+
loginValidity: options.loginValidity
|
|
879
|
+
});
|
|
880
|
+
const orgId = organizationId;
|
|
881
|
+
return await api.groupGetGroup({
|
|
882
|
+
partitionGlobalId: orgId,
|
|
883
|
+
groupId
|
|
884
|
+
});
|
|
885
|
+
})());
|
|
886
|
+
if (error) {
|
|
887
|
+
OutputFormatter.error({
|
|
888
|
+
Result: RESULTS.Failure,
|
|
889
|
+
Message: await extractErrorMessage(error),
|
|
890
|
+
Instructions: INSTRUCTIONS2.get
|
|
891
|
+
});
|
|
892
|
+
processContext.exit(1);
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
OutputFormatter.success({
|
|
896
|
+
Result: RESULTS.Success,
|
|
897
|
+
Code: "GroupDetails",
|
|
898
|
+
Data: group
|
|
899
|
+
});
|
|
900
|
+
});
|
|
901
|
+
groups.command("create").description("Create a new local group in a partition.").argument("<name>", "Group name").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(GROUPS_CREATE_EXAMPLES).trackedAction(processContext, async (name, options) => {
|
|
902
|
+
warnDeprecatedOrganization2(options.organization);
|
|
903
|
+
const [error, result] = await catchError((async () => {
|
|
904
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
905
|
+
loginValidity: options.loginValidity
|
|
906
|
+
});
|
|
907
|
+
const orgId = organizationId;
|
|
908
|
+
const createCommand = {
|
|
909
|
+
id: crypto.randomUUID(),
|
|
910
|
+
name,
|
|
911
|
+
partitionGlobalId: orgId
|
|
912
|
+
};
|
|
913
|
+
return await api.groupCreateGroup({
|
|
914
|
+
createGroupCommand: createCommand
|
|
915
|
+
});
|
|
916
|
+
})());
|
|
917
|
+
if (error) {
|
|
918
|
+
OutputFormatter.error({
|
|
919
|
+
Result: RESULTS.Failure,
|
|
920
|
+
Message: await extractErrorMessage(error),
|
|
921
|
+
Instructions: INSTRUCTIONS2.create
|
|
922
|
+
});
|
|
923
|
+
processContext.exit(1);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
OutputFormatter.success({
|
|
927
|
+
Result: RESULTS.Success,
|
|
928
|
+
Code: "GroupCreated",
|
|
929
|
+
Data: result
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
groups.command("update").description("Update a group. Use 'groups list' to retrieve the group ID.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("-n, --name <name>", "New group name").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(GROUPS_UPDATE_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
933
|
+
warnDeprecatedOrganization2(options.organization);
|
|
934
|
+
if (options.name === undefined) {
|
|
935
|
+
OutputFormatter.error({
|
|
936
|
+
Result: RESULTS.Failure,
|
|
937
|
+
Message: "No fields to update",
|
|
938
|
+
Instructions: "Provide --name to rename the group."
|
|
939
|
+
});
|
|
940
|
+
processContext.exit(1);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const [error, result] = await catchError((async () => {
|
|
944
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
945
|
+
loginValidity: options.loginValidity
|
|
946
|
+
});
|
|
947
|
+
const orgId = organizationId;
|
|
948
|
+
const updateCommand = {
|
|
949
|
+
partitionGlobalId: orgId,
|
|
950
|
+
name: options.name
|
|
951
|
+
};
|
|
952
|
+
return await api.groupUpdateGroup({
|
|
953
|
+
groupId,
|
|
954
|
+
updateGroupCommand: updateCommand
|
|
955
|
+
});
|
|
956
|
+
})());
|
|
957
|
+
if (error) {
|
|
958
|
+
OutputFormatter.error({
|
|
959
|
+
Result: RESULTS.Failure,
|
|
960
|
+
Message: await extractErrorMessage(error),
|
|
961
|
+
Instructions: INSTRUCTIONS2.update
|
|
962
|
+
});
|
|
963
|
+
processContext.exit(1);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
OutputFormatter.success({
|
|
967
|
+
Result: RESULTS.Success,
|
|
968
|
+
Code: "GroupUpdated",
|
|
969
|
+
Data: result
|
|
970
|
+
});
|
|
971
|
+
});
|
|
972
|
+
groups.command("delete").description("Delete a group. " + "Use 'groups list' to retrieve the group ID.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(GROUPS_DELETE_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
973
|
+
warnDeprecatedOrganization2(options.organization);
|
|
974
|
+
const [error] = await catchError((async () => {
|
|
975
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
976
|
+
loginValidity: options.loginValidity
|
|
977
|
+
});
|
|
978
|
+
const orgId = organizationId;
|
|
979
|
+
return await api.groupDelete({
|
|
980
|
+
partitionGlobalId: orgId,
|
|
981
|
+
groupId
|
|
982
|
+
});
|
|
983
|
+
})());
|
|
984
|
+
if (error) {
|
|
985
|
+
OutputFormatter.error({
|
|
986
|
+
Result: RESULTS.Failure,
|
|
987
|
+
Message: await extractErrorMessage(error),
|
|
988
|
+
Instructions: INSTRUCTIONS2.delete
|
|
989
|
+
});
|
|
990
|
+
processContext.exit(1);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
OutputFormatter.success({
|
|
994
|
+
Result: RESULTS.Success,
|
|
995
|
+
Code: "GroupDeleted",
|
|
996
|
+
Data: {
|
|
997
|
+
Id: groupId,
|
|
998
|
+
Status: "Deleted successfully"
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
const members = groups.command("members").description("Manage group membership. List, add, or revoke members.");
|
|
1003
|
+
members.command("list").description("List members of a group. " + "Use 'groups list' to retrieve the group ID.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("-l, --limit <number>", "Number of items to return", "50").option("--offset <number>", "Number of items to skip", "0").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(MEMBERS_LIST_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
1004
|
+
warnDeprecatedOrganization2(options.organization);
|
|
1005
|
+
const [error, result] = await catchError((async () => {
|
|
1006
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
1007
|
+
loginValidity: options.loginValidity
|
|
1008
|
+
});
|
|
1009
|
+
const orgId = organizationId;
|
|
1010
|
+
return await api.groupGetGroupMembers({
|
|
1011
|
+
partitionGlobalId: orgId,
|
|
1012
|
+
groupId,
|
|
1013
|
+
top: Number.parseInt(options.limit, 10),
|
|
1014
|
+
skip: Number.parseInt(options.offset || "0", 10)
|
|
1015
|
+
});
|
|
1016
|
+
})());
|
|
1017
|
+
if (error) {
|
|
1018
|
+
OutputFormatter.error({
|
|
1019
|
+
Result: RESULTS.Failure,
|
|
1020
|
+
Message: await extractErrorMessage(error),
|
|
1021
|
+
Instructions: INSTRUCTIONS2.listMembers
|
|
1022
|
+
});
|
|
1023
|
+
processContext.exit(1);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
OutputFormatter.success({
|
|
1027
|
+
Result: RESULTS.Success,
|
|
1028
|
+
Code: "GroupMembers",
|
|
1029
|
+
Data: result.results ?? []
|
|
1030
|
+
});
|
|
1031
|
+
});
|
|
1032
|
+
members.command("add").description("Add users to a group. Pass comma-separated user IDs.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").requiredOption("--user-ids <ids>", "Comma-separated user IDs to add").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(MEMBERS_ADD_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
1033
|
+
warnDeprecatedOrganization2(options.organization);
|
|
1034
|
+
const userIds = options.userIds.split(",").map((id) => id.trim());
|
|
1035
|
+
const [error, result] = await catchError((async () => {
|
|
1036
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
1037
|
+
loginValidity: options.loginValidity
|
|
1038
|
+
});
|
|
1039
|
+
const orgId = organizationId;
|
|
1040
|
+
const group = await api.groupGetGroup({
|
|
1041
|
+
partitionGlobalId: orgId,
|
|
1042
|
+
groupId
|
|
1043
|
+
});
|
|
1044
|
+
return await api.groupUpdateGroup({
|
|
1045
|
+
groupId,
|
|
1046
|
+
updateGroupCommand: {
|
|
1047
|
+
partitionGlobalId: orgId,
|
|
1048
|
+
name: group.name,
|
|
1049
|
+
directoryUserIDsToAdd: userIds
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
})());
|
|
1053
|
+
if (error) {
|
|
1054
|
+
OutputFormatter.error({
|
|
1055
|
+
Result: RESULTS.Failure,
|
|
1056
|
+
Message: await extractErrorMessage(error),
|
|
1057
|
+
Instructions: INSTRUCTIONS2.addMembers
|
|
1058
|
+
});
|
|
1059
|
+
processContext.exit(1);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
OutputFormatter.success({
|
|
1063
|
+
Result: RESULTS.Success,
|
|
1064
|
+
Code: "GroupMembersAdded",
|
|
1065
|
+
Data: result
|
|
1066
|
+
});
|
|
1067
|
+
});
|
|
1068
|
+
members.command("revoke").description("Revoke users from a group. Pass comma-separated user IDs.").argument("<group-id>", "Group ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").requiredOption("--user-ids <ids>", "Comma-separated user IDs to revoke").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(MEMBERS_REVOKE_EXAMPLES).trackedAction(processContext, async (groupId, options) => {
|
|
1069
|
+
warnDeprecatedOrganization2(options.organization);
|
|
1070
|
+
const userIds = options.userIds.split(",").map((id) => id.trim());
|
|
1071
|
+
const [error, result] = await catchError((async () => {
|
|
1072
|
+
const { api, organizationId } = await createApiClient(GroupApi, {
|
|
1073
|
+
loginValidity: options.loginValidity
|
|
1074
|
+
});
|
|
1075
|
+
const orgId = organizationId;
|
|
1076
|
+
const group = await api.groupGetGroup({
|
|
1077
|
+
partitionGlobalId: orgId,
|
|
1078
|
+
groupId
|
|
1079
|
+
});
|
|
1080
|
+
return await api.groupUpdateGroup({
|
|
1081
|
+
groupId,
|
|
1082
|
+
updateGroupCommand: {
|
|
1083
|
+
partitionGlobalId: orgId,
|
|
1084
|
+
name: group.name,
|
|
1085
|
+
directoryUserIDsToRemove: userIds
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
})());
|
|
1089
|
+
if (error) {
|
|
1090
|
+
OutputFormatter.error({
|
|
1091
|
+
Result: RESULTS.Failure,
|
|
1092
|
+
Message: await extractErrorMessage(error),
|
|
1093
|
+
Instructions: INSTRUCTIONS2.revokeMembers
|
|
1094
|
+
});
|
|
1095
|
+
processContext.exit(1);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
OutputFormatter.success({
|
|
1099
|
+
Result: RESULTS.Success,
|
|
1100
|
+
Code: "GroupMembersRevoked",
|
|
1101
|
+
Data: result
|
|
1102
|
+
});
|
|
1103
|
+
});
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
// ../identity-commands/src/commands/pat.ts
|
|
1107
|
+
var ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
1108
|
+
function parseIsoDate(value) {
|
|
1109
|
+
if (!ISO_DATE_PATTERN.test(value))
|
|
1110
|
+
return null;
|
|
1111
|
+
const date = new Date(value);
|
|
1112
|
+
if (Number.isNaN(date.getTime()))
|
|
1113
|
+
return null;
|
|
1114
|
+
const [year, month, day] = value.split("-").map(Number);
|
|
1115
|
+
if (date.getUTCFullYear() !== year || date.getUTCMonth() + 1 !== month || date.getUTCDate() !== day) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
return date;
|
|
1119
|
+
}
|
|
1120
|
+
function parsePaginationInt(value, _name) {
|
|
1121
|
+
const num = Number.parseInt(value, 10);
|
|
1122
|
+
if (Number.isNaN(num) || num < 0 || String(num) !== value)
|
|
1123
|
+
return null;
|
|
1124
|
+
return num;
|
|
1125
|
+
}
|
|
1126
|
+
var INSTRUCTIONS3 = {
|
|
1127
|
+
list: "Verify you are logged in to the correct organization. Run 'pat list --help' for options.",
|
|
1128
|
+
create: "Provide --description, --expiration, and --scope. Use 'pat list' to verify existing tokens.",
|
|
1129
|
+
revoke: "Verify the token ID exists. Use 'pat list' to find valid IDs.",
|
|
1130
|
+
regenerate: "Verify the token ID exists and provide a new --expiration date."
|
|
1131
|
+
};
|
|
1132
|
+
var PAT_LIST_EXAMPLES = [
|
|
1133
|
+
{
|
|
1134
|
+
Description: "List personal access tokens",
|
|
1135
|
+
Command: "uip admin pat list",
|
|
1136
|
+
Output: {
|
|
1137
|
+
Code: "PatList",
|
|
1138
|
+
Data: [
|
|
1139
|
+
{
|
|
1140
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
1141
|
+
description: "CI/CD token",
|
|
1142
|
+
expiration: "2027-01-15T00:00:00.000Z",
|
|
1143
|
+
scopes: ["OR.Folders.Read"]
|
|
1144
|
+
}
|
|
1145
|
+
]
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
];
|
|
1149
|
+
var PAT_CREATE_EXAMPLES = [
|
|
1150
|
+
{
|
|
1151
|
+
Description: "Create a personal access token",
|
|
1152
|
+
Command: 'uip admin pat create --description "CI/CD token" --expiration 2027-01-15 --scope "OR.Folders.Read,OR.Jobs.Read"',
|
|
1153
|
+
Output: {
|
|
1154
|
+
Code: "PatCreated"
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
];
|
|
1158
|
+
var PAT_REVOKE_EXAMPLES = [
|
|
1159
|
+
{
|
|
1160
|
+
Description: "Revoke a personal access token",
|
|
1161
|
+
Command: "uip admin pat revoke a1b2c3d4-0000-0000-0000-000000000001",
|
|
1162
|
+
Output: {
|
|
1163
|
+
Code: "PatRevoked"
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
];
|
|
1167
|
+
var PAT_REGENERATE_EXAMPLES = [
|
|
1168
|
+
{
|
|
1169
|
+
Description: "Regenerate a personal access token with a new expiration",
|
|
1170
|
+
Command: "uip admin pat regenerate a1b2c3d4-0000-0000-0000-000000000001 --expiration 2028-01-15",
|
|
1171
|
+
Output: {
|
|
1172
|
+
Code: "PatRegenerated"
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
];
|
|
1176
|
+
var registerPatCommand = (program) => {
|
|
1177
|
+
const pat = program.command("pat").description("Manage personal access tokens (PATs). " + "Create, list, revoke, and regenerate tokens for API authentication.");
|
|
1178
|
+
pat.command("list").description("List personal access tokens. " + "By default lists your own tokens. Use --scope all to list all tokens in the organization (requires admin).").option("--scope <scope>", 'Token scope: "all" lists all tokens in the org (admin), omit for current user only').option("--search <term>", "Filter tokens by user search term. Implies --scope all.", undefined).option("-l, --limit <number>", "Number of items to return", String(DEFAULT_PAGE_SIZE)).option("--offset <number>", "Number of items to skip", "0").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(PAT_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1179
|
+
if (options.scope !== undefined && options.scope !== "all") {
|
|
1180
|
+
OutputFormatter.error({
|
|
1181
|
+
Result: RESULTS.Failure,
|
|
1182
|
+
Message: `Invalid scope: "${options.scope}". Only "all" is supported.`,
|
|
1183
|
+
Instructions: "Use --scope all to list all tokens in the organization, or omit for your own tokens."
|
|
1184
|
+
});
|
|
1185
|
+
processContext.exit(1);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
const isAll = options.scope === "all" || options.search !== undefined;
|
|
1189
|
+
const top = parsePaginationInt(options.limit, "limit");
|
|
1190
|
+
const skip = parsePaginationInt(options.offset || "0", "offset");
|
|
1191
|
+
if (top === null || skip === null) {
|
|
1192
|
+
OutputFormatter.error({
|
|
1193
|
+
Result: RESULTS.Failure,
|
|
1194
|
+
Message: `Invalid pagination: --limit "${options.limit}", --offset "${options.offset || "0"}". Must be non-negative integers.`,
|
|
1195
|
+
Instructions: INSTRUCTIONS3.list
|
|
1196
|
+
});
|
|
1197
|
+
processContext.exit(1);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const [error, result] = await catchError((async () => {
|
|
1201
|
+
const { api, organizationId } = await createApiClient(ReferenceTokenApi, {
|
|
1202
|
+
loginValidity: options.loginValidity
|
|
1203
|
+
});
|
|
1204
|
+
if (isAll) {
|
|
1205
|
+
return await api.referenceTokenGetTokensForPartition({
|
|
1206
|
+
partitionGlobalId: organizationId,
|
|
1207
|
+
userSearchTerm: options.search,
|
|
1208
|
+
top,
|
|
1209
|
+
skip
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
const tokens = await api.referenceTokenGetTokens();
|
|
1213
|
+
return { results: tokens.slice(skip, skip + top) };
|
|
1214
|
+
})());
|
|
1215
|
+
if (error) {
|
|
1216
|
+
OutputFormatter.error({
|
|
1217
|
+
Result: RESULTS.Failure,
|
|
1218
|
+
Message: await extractErrorMessage(error),
|
|
1219
|
+
Instructions: INSTRUCTIONS3.list
|
|
1220
|
+
});
|
|
1221
|
+
processContext.exit(1);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
OutputFormatter.success({
|
|
1225
|
+
Result: RESULTS.Success,
|
|
1226
|
+
Code: "PatList",
|
|
1227
|
+
Data: result.results ?? []
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
pat.command("create").description("Create a new personal access token. " + "Returns the token value (shown only once).").requiredOption("--description <text>", "Description for the token").requiredOption("--expiration <date>", "Expiration date (ISO 8601, e.g. 2027-01-15)").requiredOption("--scope <scopes>", "Comma-separated list of scopes (e.g. OR.Folders.Read,OR.Jobs.Read)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(PAT_CREATE_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1231
|
+
const scopes = options.scope.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1232
|
+
if (scopes.length === 0) {
|
|
1233
|
+
OutputFormatter.error({
|
|
1234
|
+
Result: RESULTS.Failure,
|
|
1235
|
+
Message: "No valid scopes provided.",
|
|
1236
|
+
Instructions: "Provide at least one scope (e.g. OR.Folders.Read)."
|
|
1237
|
+
});
|
|
1238
|
+
processContext.exit(1);
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const expiration = parseIsoDate(options.expiration);
|
|
1242
|
+
if (expiration === null) {
|
|
1243
|
+
OutputFormatter.error({
|
|
1244
|
+
Result: RESULTS.Failure,
|
|
1245
|
+
Message: `Invalid expiration date: "${options.expiration}".`,
|
|
1246
|
+
Instructions: "Provide a date in YYYY-MM-DD format (e.g. 2027-01-15)."
|
|
1247
|
+
});
|
|
1248
|
+
processContext.exit(1);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
const [error, result] = await catchError((async () => {
|
|
1252
|
+
const { api } = await createApiClient(ReferenceTokenApi, {
|
|
1253
|
+
loginValidity: options.loginValidity
|
|
1254
|
+
});
|
|
1255
|
+
return await api.referenceTokenCreateToken({
|
|
1256
|
+
createReferenceTokenCommand: {
|
|
1257
|
+
description: options.description,
|
|
1258
|
+
expiration,
|
|
1259
|
+
scopes
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
})());
|
|
1263
|
+
if (error) {
|
|
1264
|
+
OutputFormatter.error({
|
|
1265
|
+
Result: RESULTS.Failure,
|
|
1266
|
+
Message: await extractErrorMessage(error),
|
|
1267
|
+
Instructions: INSTRUCTIONS3.create
|
|
1268
|
+
});
|
|
1269
|
+
processContext.exit(1);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
OutputFormatter.success({
|
|
1273
|
+
Result: RESULTS.Success,
|
|
1274
|
+
Code: "PatCreated",
|
|
1275
|
+
Data: {
|
|
1276
|
+
Token: result,
|
|
1277
|
+
Warning: "Save this token now. It will not be shown again."
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
});
|
|
1281
|
+
pat.command("revoke").description("Revoke a personal access token. " + "Use 'pat list' to retrieve the token ID.").argument("<id>", "Token ID (UUID)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(PAT_REVOKE_EXAMPLES).trackedAction(processContext, async (tokenId, options) => {
|
|
1282
|
+
const [error] = await catchError((async () => {
|
|
1283
|
+
const { api } = await createApiClient(ReferenceTokenApi, {
|
|
1284
|
+
loginValidity: options.loginValidity
|
|
1285
|
+
});
|
|
1286
|
+
await api.referenceTokenDeleteToken({
|
|
1287
|
+
referenceTokenId: tokenId
|
|
1288
|
+
});
|
|
1289
|
+
})());
|
|
1290
|
+
if (error) {
|
|
1291
|
+
OutputFormatter.error({
|
|
1292
|
+
Result: RESULTS.Failure,
|
|
1293
|
+
Message: await extractErrorMessage(error),
|
|
1294
|
+
Instructions: INSTRUCTIONS3.revoke
|
|
1295
|
+
});
|
|
1296
|
+
processContext.exit(1);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
OutputFormatter.success({
|
|
1300
|
+
Result: RESULTS.Success,
|
|
1301
|
+
Code: "PatRevoked",
|
|
1302
|
+
Data: {
|
|
1303
|
+
Id: tokenId,
|
|
1304
|
+
Status: "Revoked successfully"
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
});
|
|
1308
|
+
pat.command("regenerate").description("Regenerate a personal access token with a new expiration. " + "Returns the new token value (shown only once).").argument("<id>", "Token ID (UUID)").requiredOption("--expiration <date>", "New expiration date (ISO 8601, e.g. 2028-01-15)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(PAT_REGENERATE_EXAMPLES).trackedAction(processContext, async (tokenId, options) => {
|
|
1309
|
+
const expiration = parseIsoDate(options.expiration);
|
|
1310
|
+
if (expiration === null) {
|
|
1311
|
+
OutputFormatter.error({
|
|
1312
|
+
Result: RESULTS.Failure,
|
|
1313
|
+
Message: `Invalid expiration date: "${options.expiration}".`,
|
|
1314
|
+
Instructions: "Provide a date in YYYY-MM-DD format (e.g. 2028-01-15)."
|
|
1315
|
+
});
|
|
1316
|
+
processContext.exit(1);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const [error, result] = await catchError((async () => {
|
|
1320
|
+
const { api } = await createApiClient(ReferenceTokenApi, {
|
|
1321
|
+
loginValidity: options.loginValidity
|
|
1322
|
+
});
|
|
1323
|
+
return await api.referenceTokenRegenerateToken({
|
|
1324
|
+
referenceTokenId: tokenId,
|
|
1325
|
+
regenerateReferenceTokenCommand: {
|
|
1326
|
+
expiration
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
})());
|
|
1330
|
+
if (error) {
|
|
1331
|
+
OutputFormatter.error({
|
|
1332
|
+
Result: RESULTS.Failure,
|
|
1333
|
+
Message: await extractErrorMessage(error),
|
|
1334
|
+
Instructions: INSTRUCTIONS3.regenerate
|
|
1335
|
+
});
|
|
1336
|
+
processContext.exit(1);
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
OutputFormatter.success({
|
|
1340
|
+
Result: RESULTS.Success,
|
|
1341
|
+
Code: "PatRegenerated",
|
|
1342
|
+
Data: {
|
|
1343
|
+
Id: tokenId,
|
|
1344
|
+
Token: result,
|
|
1345
|
+
Warning: "Save this token now. It will not be shown again."
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
});
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1351
|
+
// ../identity-commands/src/commands/robot-accounts.ts
|
|
1352
|
+
var INSTRUCTIONS4 = {
|
|
1353
|
+
list: "Verify you are logged in. Run 'robot-accounts list --help' for options.",
|
|
1354
|
+
get: "Verify the robot account ID exists. Use 'robot-accounts list' to find valid IDs.",
|
|
1355
|
+
create: "Verify the robot account name is correct. Names must be unique within a partition.",
|
|
1356
|
+
update: "Verify the robot account ID is correct.",
|
|
1357
|
+
delete: "Verify the robot account ID exists. The account may have already been deleted."
|
|
1358
|
+
};
|
|
1359
|
+
var ROBOT_ACCOUNTS_LIST_EXAMPLES = [
|
|
1360
|
+
{
|
|
1361
|
+
Description: "List robot accounts in a partition",
|
|
1362
|
+
Command: "uip admin robot-accounts list --limit 5",
|
|
1363
|
+
Output: {
|
|
1364
|
+
Code: "RobotAccountList",
|
|
1365
|
+
Data: [
|
|
1366
|
+
{
|
|
1367
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
1368
|
+
name: "automation-bot",
|
|
1369
|
+
displayName: "Automation Bot",
|
|
1370
|
+
creationTime: "2026-01-15T10:00:00.000Z"
|
|
1371
|
+
}
|
|
1372
|
+
]
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
];
|
|
1376
|
+
var ROBOT_ACCOUNTS_GET_EXAMPLES = [
|
|
1377
|
+
{
|
|
1378
|
+
Description: "Get robot account details",
|
|
1379
|
+
Command: "uip admin robot-accounts get a1b2c3d4-0000-0000-0000-000000000001",
|
|
1380
|
+
Output: {
|
|
1381
|
+
Code: "RobotAccountDetails",
|
|
1382
|
+
Data: {
|
|
1383
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
1384
|
+
name: "automation-bot",
|
|
1385
|
+
displayName: "Automation Bot",
|
|
1386
|
+
creationTime: "2026-01-15T10:00:00.000Z",
|
|
1387
|
+
groupIds: []
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
];
|
|
1392
|
+
var ROBOT_ACCOUNTS_CREATE_EXAMPLES = [
|
|
1393
|
+
{
|
|
1394
|
+
Description: "Create a robot account",
|
|
1395
|
+
Command: 'uip admin robot-accounts create "automation-bot" --display-name "Automation Bot"',
|
|
1396
|
+
Output: {
|
|
1397
|
+
Code: "RobotAccountCreated",
|
|
1398
|
+
Data: {
|
|
1399
|
+
id: "a1b2c3d4-0000-0000-0000-000000000002",
|
|
1400
|
+
name: "automation-bot",
|
|
1401
|
+
displayName: "Automation Bot"
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
];
|
|
1406
|
+
var ROBOT_ACCOUNTS_UPDATE_EXAMPLES = [
|
|
1407
|
+
{
|
|
1408
|
+
Description: "Update robot account display name",
|
|
1409
|
+
Command: 'uip admin robot-accounts update a1b2c3d4-0000-0000-0000-000000000001 --display-name "Updated Bot"',
|
|
1410
|
+
Output: {
|
|
1411
|
+
Code: "RobotAccountUpdated"
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
];
|
|
1415
|
+
var ROBOT_ACCOUNTS_DELETE_EXAMPLES = [
|
|
1416
|
+
{
|
|
1417
|
+
Description: "Delete a robot account",
|
|
1418
|
+
Command: "uip admin robot-accounts delete a1b2c3d4-0000-0000-0000-000000000001",
|
|
1419
|
+
Output: {
|
|
1420
|
+
Code: "RobotAccountDeleted",
|
|
1421
|
+
Data: {
|
|
1422
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
1423
|
+
Status: "Deleted successfully"
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
];
|
|
1428
|
+
function warnDeprecatedOrganization3(org) {
|
|
1429
|
+
if (org !== undefined) {
|
|
1430
|
+
logger.warn("--organization is deprecated and ignored; organization is derived from login context");
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
var registerRobotAccountsCommand = (program) => {
|
|
1434
|
+
const robotAccounts = program.command("robot-accounts").description("Manage robot accounts. List, get, create, update, and delete robot accounts within a partition.");
|
|
1435
|
+
robotAccounts.command("list").description("List robot accounts in a partition. " + "Returns account ID, name, display name, and creation time.").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("-s, --search <term>", "Search by name").option("--sort-by <field>", "Sort results by field (e.g., 'Name')").addOption(new Option("--sort-order <direction>", "Sort direction (default: asc)").choices(["asc", "desc"])).option("-l, --limit <number>", "Number of items to return", String(DEFAULT_PAGE_SIZE)).option("--offset <number>", "Number of items to skip", "0").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(ROBOT_ACCOUNTS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1436
|
+
warnDeprecatedOrganization3(options.organization);
|
|
1437
|
+
const [error, result] = await catchError((async () => {
|
|
1438
|
+
const { api, organizationId } = await createApiClient(RobotAccountApi, {
|
|
1439
|
+
loginValidity: options.loginValidity
|
|
1440
|
+
});
|
|
1441
|
+
const orgId = organizationId;
|
|
1442
|
+
return await api.robotAccountGetRobotAccounts({
|
|
1443
|
+
partitionGlobalId: orgId,
|
|
1444
|
+
searchTerm: options.search,
|
|
1445
|
+
sortBy: options.sortBy,
|
|
1446
|
+
sortOrder: options.sortOrder,
|
|
1447
|
+
top: Number.parseInt(options.limit, 10),
|
|
1448
|
+
skip: Number.parseInt(options.offset || "0", 10)
|
|
1449
|
+
});
|
|
1450
|
+
})());
|
|
1451
|
+
if (error) {
|
|
1452
|
+
OutputFormatter.error({
|
|
1453
|
+
Result: RESULTS.Failure,
|
|
1454
|
+
Message: await extractErrorMessage(error),
|
|
1455
|
+
Instructions: INSTRUCTIONS4.list
|
|
1456
|
+
});
|
|
1457
|
+
processContext.exit(1);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
OutputFormatter.success({
|
|
1461
|
+
Result: RESULTS.Success,
|
|
1462
|
+
Code: "RobotAccountList",
|
|
1463
|
+
Data: result.results ?? []
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
robotAccounts.command("get").description("Get robot account details by ID. " + "Use 'robot-accounts list' to retrieve the ID.").argument("<robot-account-id>", "Robot account ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(ROBOT_ACCOUNTS_GET_EXAMPLES).trackedAction(processContext, async (robotAccountId, options) => {
|
|
1467
|
+
warnDeprecatedOrganization3(options.organization);
|
|
1468
|
+
const [error, account] = await catchError((async () => {
|
|
1469
|
+
const { api, organizationId } = await createApiClient(RobotAccountApi, {
|
|
1470
|
+
loginValidity: options.loginValidity
|
|
1471
|
+
});
|
|
1472
|
+
const orgId = organizationId;
|
|
1473
|
+
return await api.robotAccountGetRobotAccount({
|
|
1474
|
+
partitionGlobalId: orgId,
|
|
1475
|
+
robotAccountId
|
|
1476
|
+
});
|
|
1477
|
+
})());
|
|
1478
|
+
if (error) {
|
|
1479
|
+
OutputFormatter.error({
|
|
1480
|
+
Result: RESULTS.Failure,
|
|
1481
|
+
Message: await extractErrorMessage(error),
|
|
1482
|
+
Instructions: INSTRUCTIONS4.get
|
|
1483
|
+
});
|
|
1484
|
+
processContext.exit(1);
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
OutputFormatter.success({
|
|
1488
|
+
Result: RESULTS.Success,
|
|
1489
|
+
Code: "RobotAccountDetails",
|
|
1490
|
+
Data: account
|
|
1491
|
+
});
|
|
1492
|
+
});
|
|
1493
|
+
robotAccounts.command("create").description("Create a new robot account in a partition.").argument("<name>", "Robot account name").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--display-name <display-name>", "Display name").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(ROBOT_ACCOUNTS_CREATE_EXAMPLES).trackedAction(processContext, async (name, options) => {
|
|
1494
|
+
warnDeprecatedOrganization3(options.organization);
|
|
1495
|
+
const [error, result] = await catchError((async () => {
|
|
1496
|
+
const { api, organizationId } = await createApiClient(RobotAccountApi, {
|
|
1497
|
+
loginValidity: options.loginValidity
|
|
1498
|
+
});
|
|
1499
|
+
const orgId = organizationId;
|
|
1500
|
+
const createCommand = {
|
|
1501
|
+
name,
|
|
1502
|
+
displayName: options.displayName ?? name,
|
|
1503
|
+
partitionGlobalId: orgId
|
|
1504
|
+
};
|
|
1505
|
+
return await api.robotAccountCreateRobotAccount({
|
|
1506
|
+
createRobotAccountCommand: createCommand
|
|
1507
|
+
});
|
|
1508
|
+
})());
|
|
1509
|
+
if (error) {
|
|
1510
|
+
OutputFormatter.error({
|
|
1511
|
+
Result: RESULTS.Failure,
|
|
1512
|
+
Message: await extractErrorMessage(error),
|
|
1513
|
+
Instructions: INSTRUCTIONS4.create
|
|
1514
|
+
});
|
|
1515
|
+
processContext.exit(1);
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
OutputFormatter.success({
|
|
1519
|
+
Result: RESULTS.Success,
|
|
1520
|
+
Code: "RobotAccountCreated",
|
|
1521
|
+
Data: result
|
|
1522
|
+
});
|
|
1523
|
+
});
|
|
1524
|
+
robotAccounts.command("update").description("Update a robot account. Use 'robot-accounts list' to retrieve the ID.").argument("<robot-account-id>", "Robot account ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--display-name <display-name>", "New display name").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(ROBOT_ACCOUNTS_UPDATE_EXAMPLES).trackedAction(processContext, async (robotAccountId, options) => {
|
|
1525
|
+
warnDeprecatedOrganization3(options.organization);
|
|
1526
|
+
if (options.displayName === undefined) {
|
|
1527
|
+
OutputFormatter.error({
|
|
1528
|
+
Result: RESULTS.Failure,
|
|
1529
|
+
Message: "No fields to update",
|
|
1530
|
+
Instructions: "Provide --display-name to update the robot account."
|
|
1531
|
+
});
|
|
1532
|
+
processContext.exit(1);
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
const [error, result] = await catchError((async () => {
|
|
1536
|
+
const { api, organizationId } = await createApiClient(RobotAccountApi, {
|
|
1537
|
+
loginValidity: options.loginValidity
|
|
1538
|
+
});
|
|
1539
|
+
const orgId = organizationId;
|
|
1540
|
+
const updateCommand = {
|
|
1541
|
+
partitionGlobalId: orgId,
|
|
1542
|
+
displayName: options.displayName
|
|
1543
|
+
};
|
|
1544
|
+
return await api.robotAccountUpdateRobotAccount({
|
|
1545
|
+
robotAccountId,
|
|
1546
|
+
updateRobotAccountCommand: updateCommand
|
|
1547
|
+
});
|
|
1548
|
+
})());
|
|
1549
|
+
if (error) {
|
|
1550
|
+
OutputFormatter.error({
|
|
1551
|
+
Result: RESULTS.Failure,
|
|
1552
|
+
Message: await extractErrorMessage(error),
|
|
1553
|
+
Instructions: INSTRUCTIONS4.update
|
|
1554
|
+
});
|
|
1555
|
+
processContext.exit(1);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
OutputFormatter.success({
|
|
1559
|
+
Result: RESULTS.Success,
|
|
1560
|
+
Code: "RobotAccountUpdated",
|
|
1561
|
+
Data: result
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
robotAccounts.command("delete").description("Delete a robot account. " + "Use 'robot-accounts list' to retrieve the ID.").argument("<robot-account-id>", "Robot account ID (UUID)").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(ROBOT_ACCOUNTS_DELETE_EXAMPLES).trackedAction(processContext, async (robotAccountId, options) => {
|
|
1565
|
+
warnDeprecatedOrganization3(options.organization);
|
|
1566
|
+
const [error] = await catchError((async () => {
|
|
1567
|
+
const { api, organizationId } = await createApiClient(RobotAccountApi, {
|
|
1568
|
+
loginValidity: options.loginValidity
|
|
1569
|
+
});
|
|
1570
|
+
const orgId = organizationId;
|
|
1571
|
+
await api.robotAccountDelete({
|
|
1572
|
+
partitionGlobalId: orgId,
|
|
1573
|
+
robotAccountId
|
|
1574
|
+
});
|
|
1575
|
+
})());
|
|
1576
|
+
if (error) {
|
|
1577
|
+
OutputFormatter.error({
|
|
1578
|
+
Result: RESULTS.Failure,
|
|
1579
|
+
Message: await extractErrorMessage(error),
|
|
1580
|
+
Instructions: INSTRUCTIONS4.delete
|
|
1581
|
+
});
|
|
1582
|
+
processContext.exit(1);
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
OutputFormatter.success({
|
|
1586
|
+
Result: RESULTS.Success,
|
|
1587
|
+
Code: "RobotAccountDeleted",
|
|
1588
|
+
Data: {
|
|
1589
|
+
Id: robotAccountId,
|
|
1590
|
+
Status: "Deleted successfully"
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
// ../identity-commands/src/commands/scopes.ts
|
|
1597
|
+
var INSTRUCTIONS5 = {
|
|
1598
|
+
list: "Verify you are logged in. Run 'uip login status' to check."
|
|
1599
|
+
};
|
|
1600
|
+
var SCOPES_LIST_EXAMPLES = [
|
|
1601
|
+
{
|
|
1602
|
+
Description: "List available OAuth2 scopes",
|
|
1603
|
+
Command: "uip admin scopes list",
|
|
1604
|
+
Output: {
|
|
1605
|
+
Code: "ScopesList",
|
|
1606
|
+
Data: [
|
|
1607
|
+
{
|
|
1608
|
+
name: "UiPath.Orchestrator",
|
|
1609
|
+
displayName: "Orchestrator API Access",
|
|
1610
|
+
scopes: [
|
|
1611
|
+
{
|
|
1612
|
+
name: "OR.Folders",
|
|
1613
|
+
displayName: "OR.Folders",
|
|
1614
|
+
type: "user"
|
|
1615
|
+
}
|
|
1616
|
+
]
|
|
1617
|
+
}
|
|
1618
|
+
]
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
];
|
|
1622
|
+
var registerScopesCommand = (program) => {
|
|
1623
|
+
const scopes = program.command("scopes").description("Browse available OAuth2 scopes for external apps and personal access tokens.");
|
|
1624
|
+
scopes.command("list").description("List all available OAuth2 scopes grouped by resource.").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(SCOPES_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1625
|
+
const [error, result] = await catchError((async () => {
|
|
1626
|
+
const { api } = await createApiClient(ExternalApiResourceApi, {
|
|
1627
|
+
loginValidity: options.loginValidity
|
|
1628
|
+
});
|
|
1629
|
+
return await api.externalApiResourceGet();
|
|
1630
|
+
})());
|
|
1631
|
+
if (error) {
|
|
1632
|
+
OutputFormatter.error({
|
|
1633
|
+
Result: RESULTS.Failure,
|
|
1634
|
+
Message: await extractErrorMessage(error),
|
|
1635
|
+
Instructions: INSTRUCTIONS5.list
|
|
1636
|
+
});
|
|
1637
|
+
processContext.exit(1);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
OutputFormatter.success({
|
|
1641
|
+
Result: RESULTS.Success,
|
|
1642
|
+
Code: "ScopesList",
|
|
1643
|
+
Data: result ?? []
|
|
1644
|
+
});
|
|
1645
|
+
});
|
|
1646
|
+
};
|
|
1647
|
+
|
|
1648
|
+
// ../identity-commands/src/commands/smtp.ts
|
|
1649
|
+
var SMTP_SETTING_KEYS = [
|
|
1650
|
+
"Email.Smtp.Host",
|
|
1651
|
+
"Email.Smtp.Port",
|
|
1652
|
+
"Email.Smtp.UserName",
|
|
1653
|
+
"Email.Smtp.Password",
|
|
1654
|
+
"Email.Smtp.Domain",
|
|
1655
|
+
"Email.Smtp.EnableSsl",
|
|
1656
|
+
"Email.Smtp.UseDefaultCredentials",
|
|
1657
|
+
"Email.Smtp.FromEmail",
|
|
1658
|
+
"Email.Smtp.FromDisplayName",
|
|
1659
|
+
"Email.Smtp.ConnectionTimeout"
|
|
1660
|
+
];
|
|
1661
|
+
var SMTP_SETTING_KEYS_READ = SMTP_SETTING_KEYS.filter((k) => k !== "Email.Smtp.Password");
|
|
1662
|
+
var INSTRUCTIONS6 = {
|
|
1663
|
+
get: "Verify you are logged in to the correct organization. Run 'smtp get --help' for options.",
|
|
1664
|
+
update: "Verify you are logged in and at least one SMTP field is provided.",
|
|
1665
|
+
delete: "Verify you are logged in. The settings may have already been deleted.",
|
|
1666
|
+
test: "Verify --recipient is a valid email address. Use 'smtp get' to check current settings."
|
|
1667
|
+
};
|
|
1668
|
+
function settingsToSmtpData(settings) {
|
|
1669
|
+
const data = {};
|
|
1670
|
+
for (const setting of settings) {
|
|
1671
|
+
switch (setting.key) {
|
|
1672
|
+
case "Email.Smtp.Host":
|
|
1673
|
+
data.host = setting.value ?? undefined;
|
|
1674
|
+
break;
|
|
1675
|
+
case "Email.Smtp.Port":
|
|
1676
|
+
data.port = setting.value ?? undefined;
|
|
1677
|
+
break;
|
|
1678
|
+
case "Email.Smtp.UserName":
|
|
1679
|
+
data.userName = setting.value ?? undefined;
|
|
1680
|
+
break;
|
|
1681
|
+
case "Email.Smtp.Domain":
|
|
1682
|
+
data.domain = setting.value ?? undefined;
|
|
1683
|
+
break;
|
|
1684
|
+
case "Email.Smtp.EnableSsl":
|
|
1685
|
+
data.enableSsl = setting.value ?? undefined;
|
|
1686
|
+
break;
|
|
1687
|
+
case "Email.Smtp.UseDefaultCredentials":
|
|
1688
|
+
data.useDefaultCredentials = setting.value ?? undefined;
|
|
1689
|
+
break;
|
|
1690
|
+
case "Email.Smtp.FromEmail":
|
|
1691
|
+
data.fromAddress = setting.value ?? undefined;
|
|
1692
|
+
break;
|
|
1693
|
+
case "Email.Smtp.FromDisplayName":
|
|
1694
|
+
data.fromDisplayName = setting.value ?? undefined;
|
|
1695
|
+
break;
|
|
1696
|
+
case "Email.Smtp.ConnectionTimeout":
|
|
1697
|
+
data.connectionTimeout = setting.value ?? undefined;
|
|
1698
|
+
break;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return data;
|
|
1702
|
+
}
|
|
1703
|
+
function settingsToSmtpConfig(settings) {
|
|
1704
|
+
const config = {};
|
|
1705
|
+
for (const setting of settings) {
|
|
1706
|
+
const val = setting.value ?? undefined;
|
|
1707
|
+
switch (setting.key) {
|
|
1708
|
+
case "Email.Smtp.Host":
|
|
1709
|
+
config.host = val;
|
|
1710
|
+
break;
|
|
1711
|
+
case "Email.Smtp.Port":
|
|
1712
|
+
config.port = val ? Number.parseInt(val, 10) : undefined;
|
|
1713
|
+
break;
|
|
1714
|
+
case "Email.Smtp.UserName":
|
|
1715
|
+
config.userName = val;
|
|
1716
|
+
break;
|
|
1717
|
+
case "Email.Smtp.Password":
|
|
1718
|
+
config.password = val;
|
|
1719
|
+
break;
|
|
1720
|
+
case "Email.Smtp.Domain":
|
|
1721
|
+
config.domain = val;
|
|
1722
|
+
break;
|
|
1723
|
+
case "Email.Smtp.EnableSsl":
|
|
1724
|
+
config.enableSsl = val?.toLowerCase() === "true";
|
|
1725
|
+
break;
|
|
1726
|
+
case "Email.Smtp.UseDefaultCredentials":
|
|
1727
|
+
config.useDefaultCredentials = val?.toLowerCase() === "true";
|
|
1728
|
+
break;
|
|
1729
|
+
case "Email.Smtp.FromEmail":
|
|
1730
|
+
config.fromAddress = val;
|
|
1731
|
+
break;
|
|
1732
|
+
case "Email.Smtp.FromDisplayName":
|
|
1733
|
+
config.fromDisplayName = val;
|
|
1734
|
+
break;
|
|
1735
|
+
case "Email.Smtp.ConnectionTimeout":
|
|
1736
|
+
config.connectionTimeout = val ? Number.parseInt(val, 10) : undefined;
|
|
1737
|
+
break;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
return config;
|
|
1741
|
+
}
|
|
1742
|
+
function stripUndefined(obj) {
|
|
1743
|
+
const result = {};
|
|
1744
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1745
|
+
if (value !== undefined) {
|
|
1746
|
+
result[key] = value;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return result;
|
|
1750
|
+
}
|
|
1751
|
+
function buildSettingsFromOptions(options) {
|
|
1752
|
+
const settings = [];
|
|
1753
|
+
if (options.host !== undefined) {
|
|
1754
|
+
settings.push({ key: "Email.Smtp.Host", value: options.host });
|
|
1755
|
+
}
|
|
1756
|
+
if (options.port !== undefined) {
|
|
1757
|
+
settings.push({ key: "Email.Smtp.Port", value: options.port });
|
|
1758
|
+
}
|
|
1759
|
+
if (options.username !== undefined) {
|
|
1760
|
+
settings.push({ key: "Email.Smtp.UserName", value: options.username });
|
|
1761
|
+
}
|
|
1762
|
+
if (options.password !== undefined) {
|
|
1763
|
+
settings.push({ key: "Email.Smtp.Password", value: options.password });
|
|
1764
|
+
}
|
|
1765
|
+
if (options.domain !== undefined) {
|
|
1766
|
+
settings.push({ key: "Email.Smtp.Domain", value: options.domain });
|
|
1767
|
+
}
|
|
1768
|
+
if (options.enableSsl !== undefined) {
|
|
1769
|
+
settings.push({
|
|
1770
|
+
key: "Email.Smtp.EnableSsl",
|
|
1771
|
+
value: options.enableSsl
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
if (options.useDefaultCredentials !== undefined) {
|
|
1775
|
+
settings.push({
|
|
1776
|
+
key: "Email.Smtp.UseDefaultCredentials",
|
|
1777
|
+
value: options.useDefaultCredentials
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
if (options.fromAddress !== undefined) {
|
|
1781
|
+
settings.push({
|
|
1782
|
+
key: "Email.Smtp.FromEmail",
|
|
1783
|
+
value: options.fromAddress
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
if (options.fromDisplayName !== undefined) {
|
|
1787
|
+
settings.push({
|
|
1788
|
+
key: "Email.Smtp.FromDisplayName",
|
|
1789
|
+
value: options.fromDisplayName
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
if (options.connectionTimeout !== undefined) {
|
|
1793
|
+
settings.push({
|
|
1794
|
+
key: "Email.Smtp.ConnectionTimeout",
|
|
1795
|
+
value: options.connectionTimeout
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
return settings;
|
|
1799
|
+
}
|
|
1800
|
+
function buildSmtpConfig(options) {
|
|
1801
|
+
const hasAny = options.host !== undefined || options.port !== undefined || options.username !== undefined || options.password !== undefined || options.domain !== undefined || options.enableSsl !== undefined || options.useDefaultCredentials !== undefined || options.fromAddress !== undefined || options.fromDisplayName !== undefined || options.connectionTimeout !== undefined;
|
|
1802
|
+
if (!hasAny) {
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
host: options.host,
|
|
1807
|
+
port: options.port !== undefined ? Number.parseInt(options.port, 10) : undefined,
|
|
1808
|
+
userName: options.username,
|
|
1809
|
+
password: options.password,
|
|
1810
|
+
domain: options.domain,
|
|
1811
|
+
enableSsl: options.enableSsl !== undefined ? options.enableSsl === "true" : undefined,
|
|
1812
|
+
useDefaultCredentials: options.useDefaultCredentials !== undefined ? options.useDefaultCredentials === "true" : undefined,
|
|
1813
|
+
fromAddress: options.fromAddress,
|
|
1814
|
+
fromDisplayName: options.fromDisplayName,
|
|
1815
|
+
connectionTimeout: options.connectionTimeout !== undefined ? Number.parseInt(options.connectionTimeout, 10) : undefined
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
var SMTP_GET_EXAMPLES = [
|
|
1819
|
+
{
|
|
1820
|
+
Description: "Get current SMTP settings",
|
|
1821
|
+
Command: "uip admin smtp get",
|
|
1822
|
+
Output: {
|
|
1823
|
+
Code: "SmtpSettings",
|
|
1824
|
+
Data: {
|
|
1825
|
+
host: "smtp.example.com",
|
|
1826
|
+
port: "587",
|
|
1827
|
+
userName: "noreply@example.com",
|
|
1828
|
+
enableSsl: "True",
|
|
1829
|
+
fromAddress: "noreply@example.com",
|
|
1830
|
+
fromDisplayName: "UiPath Platform"
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
];
|
|
1835
|
+
var SMTP_UPDATE_EXAMPLES = [
|
|
1836
|
+
{
|
|
1837
|
+
Description: "Update SMTP host and port",
|
|
1838
|
+
Command: "uip admin smtp update --host smtp.example.com --port 587",
|
|
1839
|
+
Output: {
|
|
1840
|
+
Code: "SmtpSettingsUpdated"
|
|
1841
|
+
}
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
Description: "Configure full SMTP settings",
|
|
1845
|
+
Command: "uip admin smtp update --host smtp.example.com --port 587 --username user@example.com --password secret --from-address noreply@example.com --enable-ssl true",
|
|
1846
|
+
Output: {
|
|
1847
|
+
Code: "SmtpSettingsUpdated"
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
];
|
|
1851
|
+
var SMTP_DELETE_EXAMPLES = [
|
|
1852
|
+
{
|
|
1853
|
+
Description: "Delete all SMTP settings",
|
|
1854
|
+
Command: "uip admin smtp delete",
|
|
1855
|
+
Output: {
|
|
1856
|
+
Code: "SmtpSettingsDeleted",
|
|
1857
|
+
Data: {
|
|
1858
|
+
Status: "Deleted successfully"
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
];
|
|
1863
|
+
var SMTP_TEST_EXAMPLES = [
|
|
1864
|
+
{
|
|
1865
|
+
Description: "Test current SMTP settings by sending a test email",
|
|
1866
|
+
Command: "uip admin smtp test --recipient admin@example.com",
|
|
1867
|
+
Output: {
|
|
1868
|
+
Code: "SmtpTestSent",
|
|
1869
|
+
Data: {
|
|
1870
|
+
Recipient: "admin@example.com",
|
|
1871
|
+
Status: "Test email sent successfully"
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
Description: "Test with custom SMTP settings without saving them",
|
|
1877
|
+
Command: "uip admin smtp test --recipient admin@example.com --host smtp.example.com --port 587 --username user@example.com --password secret",
|
|
1878
|
+
Output: {
|
|
1879
|
+
Code: "SmtpTestSent",
|
|
1880
|
+
Data: {
|
|
1881
|
+
Recipient: "admin@example.com",
|
|
1882
|
+
Status: "Test email sent successfully"
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
];
|
|
1887
|
+
function parsePort(value) {
|
|
1888
|
+
const n = Number(value);
|
|
1889
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
1890
|
+
throw new Error("Port must be an integer between 1 and 65535.");
|
|
1891
|
+
}
|
|
1892
|
+
return String(n);
|
|
1893
|
+
}
|
|
1894
|
+
function parseTimeout(value) {
|
|
1895
|
+
const n = Number(value);
|
|
1896
|
+
if (!Number.isInteger(n) || n < 1 || n > 300000) {
|
|
1897
|
+
throw new Error("Connection timeout must be an integer between 1 and 300000.");
|
|
1898
|
+
}
|
|
1899
|
+
return String(n);
|
|
1900
|
+
}
|
|
1901
|
+
function addSmtpOptions(command) {
|
|
1902
|
+
return command.option("--host <host>", "SMTP server hostname").option("--port <port>", "SMTP server port", parsePort).option("--username <username>", "SMTP authentication username").option("--password <password>", "SMTP authentication password").option("--domain <domain>", "SMTP authentication domain").addOption(new Option("--enable-ssl <value>", "Enable SSL/TLS (true or false)").choices(["true", "false"])).addOption(new Option("--use-default-credentials <value>", "Use Windows default credentials (true or false)").choices(["true", "false"])).option("--from-address <email>", "Sender email address").option("--from-display-name <name>", "Sender display name").option("--connection-timeout <ms>", "Connection timeout in milliseconds (1-300000)", parseTimeout);
|
|
1903
|
+
}
|
|
1904
|
+
var registerSmtpCommand = (program) => {
|
|
1905
|
+
const smtp = program.command("smtp").description("Manage SMTP email settings. Get, update, delete, and test SMTP configuration.");
|
|
1906
|
+
smtp.command("get").description("Get current SMTP settings for the organization. " + "Returns host, port, sender address, SSL, and timeout configuration. " + "Password values are not returned.").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(SMTP_GET_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1907
|
+
const [error, result] = await catchError((async () => {
|
|
1908
|
+
const { api, organizationId } = await createApiClient(SettingApi, {
|
|
1909
|
+
loginValidity: options.loginValidity
|
|
1910
|
+
});
|
|
1911
|
+
const orgId = organizationId;
|
|
1912
|
+
return await api.settingGetSettingsBulk({
|
|
1913
|
+
key: SMTP_SETTING_KEYS_READ,
|
|
1914
|
+
partitionGlobalId: orgId
|
|
1915
|
+
});
|
|
1916
|
+
})());
|
|
1917
|
+
if (error) {
|
|
1918
|
+
OutputFormatter.error({
|
|
1919
|
+
Result: RESULTS.Failure,
|
|
1920
|
+
Message: await extractErrorMessage(error),
|
|
1921
|
+
Instructions: INSTRUCTIONS6.get
|
|
1922
|
+
});
|
|
1923
|
+
processContext.exit(1);
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
OutputFormatter.success({
|
|
1927
|
+
Result: RESULTS.Success,
|
|
1928
|
+
Code: "SmtpSettings",
|
|
1929
|
+
Data: settingsToSmtpData(result)
|
|
1930
|
+
});
|
|
1931
|
+
});
|
|
1932
|
+
const update = smtp.command("update").description("Update SMTP settings for the organization. " + "Only provided fields are updated; unspecified fields remain unchanged.").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10));
|
|
1933
|
+
addSmtpOptions(update).examples(SMTP_UPDATE_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1934
|
+
const userSettings = buildSettingsFromOptions(options);
|
|
1935
|
+
if (userSettings.length === 0) {
|
|
1936
|
+
OutputFormatter.error({
|
|
1937
|
+
Result: RESULTS.Failure,
|
|
1938
|
+
Message: "No SMTP fields to update",
|
|
1939
|
+
Instructions: "Provide at least one SMTP option (e.g. --host, --port). Run 'smtp update --help' for all options."
|
|
1940
|
+
});
|
|
1941
|
+
processContext.exit(1);
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
const [error, result] = await catchError((async () => {
|
|
1945
|
+
const { api, organizationId } = await createApiClient(SettingApi, {
|
|
1946
|
+
loginValidity: options.loginValidity
|
|
1947
|
+
});
|
|
1948
|
+
const orgId = organizationId;
|
|
1949
|
+
const existing = await api.settingGetSettingsBulk({
|
|
1950
|
+
key: SMTP_SETTING_KEYS_READ,
|
|
1951
|
+
partitionGlobalId: orgId
|
|
1952
|
+
});
|
|
1953
|
+
const merged = new Map;
|
|
1954
|
+
for (const s of existing) {
|
|
1955
|
+
if (s.key) {
|
|
1956
|
+
merged.set(s.key, s.value ?? null);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
for (const s of userSettings) {
|
|
1960
|
+
merged.set(s.key, s.value ?? null);
|
|
1961
|
+
}
|
|
1962
|
+
const settings = [...merged.entries()].map(([key, value]) => ({ key, value }));
|
|
1963
|
+
return await api.settingUpdateSettings({
|
|
1964
|
+
bulkCreateOrUpdateSettingsCommand: {
|
|
1965
|
+
settings,
|
|
1966
|
+
partitionGlobalId: orgId
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
})());
|
|
1970
|
+
if (error) {
|
|
1971
|
+
OutputFormatter.error({
|
|
1972
|
+
Result: RESULTS.Failure,
|
|
1973
|
+
Message: await extractErrorMessage(error),
|
|
1974
|
+
Instructions: INSTRUCTIONS6.update
|
|
1975
|
+
});
|
|
1976
|
+
processContext.exit(1);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
OutputFormatter.success({
|
|
1980
|
+
Result: RESULTS.Success,
|
|
1981
|
+
Code: "SmtpSettingsUpdated",
|
|
1982
|
+
Data: settingsToSmtpData(result)
|
|
1983
|
+
});
|
|
1984
|
+
});
|
|
1985
|
+
smtp.command("delete").description("Delete all SMTP settings for the organization. " + "This removes all email configuration.").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(SMTP_DELETE_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
1986
|
+
const [error] = await catchError((async () => {
|
|
1987
|
+
const { api, organizationId } = await createApiClient(SettingApi, {
|
|
1988
|
+
loginValidity: options.loginValidity
|
|
1989
|
+
});
|
|
1990
|
+
const orgId = organizationId;
|
|
1991
|
+
return await api.settingBulkDelete({
|
|
1992
|
+
bulkDeleteSettingsCommand: {
|
|
1993
|
+
settings: SMTP_SETTING_KEYS,
|
|
1994
|
+
partitionGlobalId: orgId
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
})());
|
|
1998
|
+
if (error) {
|
|
1999
|
+
OutputFormatter.error({
|
|
2000
|
+
Result: RESULTS.Failure,
|
|
2001
|
+
Message: await extractErrorMessage(error),
|
|
2002
|
+
Instructions: INSTRUCTIONS6.delete
|
|
2003
|
+
});
|
|
2004
|
+
processContext.exit(1);
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
OutputFormatter.success({
|
|
2008
|
+
Result: RESULTS.Success,
|
|
2009
|
+
Code: "SmtpSettingsDeleted",
|
|
2010
|
+
Data: {
|
|
2011
|
+
Status: "Deleted successfully"
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
});
|
|
2015
|
+
const test = smtp.command("test").description("Test SMTP settings by sending a test email to a recipient. " + "Uses saved settings by default. " + "Pass SMTP options to test custom settings without saving them.").requiredOption("--recipient <email>", "Email address to send the test email to").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10));
|
|
2016
|
+
addSmtpOptions(test).examples(SMTP_TEST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
2017
|
+
const customConfig = buildSmtpConfig(options);
|
|
2018
|
+
if (customConfig !== undefined && options.password === undefined && options.useDefaultCredentials !== "true") {
|
|
2019
|
+
OutputFormatter.error({
|
|
2020
|
+
Result: RESULTS.Failure,
|
|
2021
|
+
Message: "The --password option is required when testing with custom SMTP settings (unless --use-default-credentials true)",
|
|
2022
|
+
Instructions: "Provide --password, --use-default-credentials true, or omit all SMTP options to test with saved settings."
|
|
2023
|
+
});
|
|
2024
|
+
processContext.exit(1);
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
const [error] = await catchError((async () => {
|
|
2028
|
+
const { api, organizationId } = await createApiClient(SettingApi, {
|
|
2029
|
+
loginValidity: options.loginValidity
|
|
2030
|
+
});
|
|
2031
|
+
let smtpSettings = customConfig;
|
|
2032
|
+
if (smtpSettings !== undefined) {
|
|
2033
|
+
const saved = await api.settingGetSettingsBulk({
|
|
2034
|
+
key: SMTP_SETTING_KEYS_READ,
|
|
2035
|
+
partitionGlobalId: organizationId
|
|
2036
|
+
});
|
|
2037
|
+
const base = settingsToSmtpConfig(saved);
|
|
2038
|
+
smtpSettings = {
|
|
2039
|
+
...base,
|
|
2040
|
+
...stripUndefined(smtpSettings)
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
return await api.settingVerifySmtpSetting({
|
|
2044
|
+
verifySmtpSettingsCommand: {
|
|
2045
|
+
recipient: options.recipient,
|
|
2046
|
+
smtpSettings
|
|
2047
|
+
}
|
|
2048
|
+
});
|
|
2049
|
+
})());
|
|
2050
|
+
if (error) {
|
|
2051
|
+
OutputFormatter.error({
|
|
2052
|
+
Result: RESULTS.Failure,
|
|
2053
|
+
Message: await extractErrorMessage(error),
|
|
2054
|
+
Instructions: INSTRUCTIONS6.test
|
|
2055
|
+
});
|
|
2056
|
+
processContext.exit(1);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
OutputFormatter.success({
|
|
2060
|
+
Result: RESULTS.Success,
|
|
2061
|
+
Code: "SmtpTestSent",
|
|
2062
|
+
Data: {
|
|
2063
|
+
Recipient: options.recipient,
|
|
2064
|
+
Status: "Test email sent successfully"
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
});
|
|
2068
|
+
};
|
|
2069
|
+
|
|
2070
|
+
// ../identity-commands/src/commands/users.ts
|
|
2071
|
+
var INSTRUCTIONS7 = {
|
|
2072
|
+
list: "Verify you are logged in. Run 'users list --help' for options.",
|
|
2073
|
+
get: "Verify the user ID exists. Use 'users list' to find valid IDs.",
|
|
2074
|
+
create: "Verify --email is correct. The username may already exist.",
|
|
2075
|
+
update: "Verify the user ID exists. Use 'users list' to find valid IDs.",
|
|
2076
|
+
delete: "Verify the user ID exists. The user may have already been deleted.",
|
|
2077
|
+
invite: "Verify the email address is valid and the org allows invitations."
|
|
2078
|
+
};
|
|
2079
|
+
var USERS_LIST_EXAMPLES = [
|
|
2080
|
+
{
|
|
2081
|
+
Description: "List users in a partition",
|
|
2082
|
+
Command: "uip admin users list --limit 5",
|
|
2083
|
+
Output: {
|
|
2084
|
+
Code: "UserList",
|
|
2085
|
+
Data: [
|
|
2086
|
+
{
|
|
2087
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
2088
|
+
userName: "admin@example.com",
|
|
2089
|
+
email: "admin@example.com",
|
|
2090
|
+
name: "Admin",
|
|
2091
|
+
surname: "User",
|
|
2092
|
+
displayName: "Admin User",
|
|
2093
|
+
creationTime: "2026-01-15T10:00:00.000Z"
|
|
2094
|
+
}
|
|
2095
|
+
]
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
];
|
|
2099
|
+
var USERS_GET_EXAMPLES = [
|
|
2100
|
+
{
|
|
2101
|
+
Description: "Get user details by ID",
|
|
2102
|
+
Command: "uip admin users get a1b2c3d4-0000-0000-0000-000000000001",
|
|
2103
|
+
Output: {
|
|
2104
|
+
Code: "UserDetails",
|
|
2105
|
+
Data: {
|
|
2106
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
2107
|
+
userName: "admin@example.com",
|
|
2108
|
+
email: "admin@example.com",
|
|
2109
|
+
name: "Admin",
|
|
2110
|
+
surname: "User",
|
|
2111
|
+
displayName: "Admin User",
|
|
2112
|
+
creationTime: "2026-01-15T10:00:00.000Z"
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
];
|
|
2117
|
+
var USERS_CREATE_EXAMPLES = [
|
|
2118
|
+
{
|
|
2119
|
+
Description: "Create a new user",
|
|
2120
|
+
Command: 'uip admin users create newuser --email "user@example.com" --name "John" --surname "Doe"',
|
|
2121
|
+
Output: {
|
|
2122
|
+
Code: "UserCreated",
|
|
2123
|
+
Data: {
|
|
2124
|
+
Succeeded: true
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
];
|
|
2129
|
+
var USERS_DELETE_EXAMPLES = [
|
|
2130
|
+
{
|
|
2131
|
+
Description: "Delete a user by ID",
|
|
2132
|
+
Command: "uip admin users delete a1b2c3d4-0000-0000-0000-000000000001",
|
|
2133
|
+
Output: {
|
|
2134
|
+
Code: "UserDeleted",
|
|
2135
|
+
Data: {
|
|
2136
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
2137
|
+
Status: "Deleted successfully"
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
];
|
|
2142
|
+
var USERS_UPDATE_EXAMPLES = [
|
|
2143
|
+
{
|
|
2144
|
+
Description: "Update user name and email",
|
|
2145
|
+
Command: 'uip admin users update a1b2c3d4-0000-0000-0000-000000000001 --name "Jane" --surname "Doe" --email "jane@example.com"',
|
|
2146
|
+
Output: {
|
|
2147
|
+
Code: "UserUpdated"
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
];
|
|
2151
|
+
var USERS_INVITE_EXAMPLES = [
|
|
2152
|
+
{
|
|
2153
|
+
Description: "Invite a user by email",
|
|
2154
|
+
Command: 'uip admin users invite --email "user@example.com"',
|
|
2155
|
+
Output: {
|
|
2156
|
+
Code: "UsersInvited",
|
|
2157
|
+
Data: {
|
|
2158
|
+
succeeded: true
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
];
|
|
2163
|
+
function warnDeprecatedOrganization4(org) {
|
|
2164
|
+
if (org !== undefined) {
|
|
2165
|
+
logger.warn("--organization is deprecated and ignored; organization is derived from login context");
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
var registerUsersCommand = (program) => {
|
|
2169
|
+
const users = program.command("users").description("Manage users. List, get, create, update, and delete users.");
|
|
2170
|
+
users.command("list").description("List users in a partition. " + "Returns user ID, username, email, name, and creation time.").option("--organization <id>", "(deprecated, ignored) organization is derived from login").option("-s, --search <term>", "Search users by name or email").option("--sort-by <field>", "Sort results by field (e.g., 'UserName', 'Email')").addOption(new Option("--sort-order <direction>", "Sort direction (default: asc)").choices(["asc", "desc"])).option("-l, --limit <number>", "Number of items to return", String(DEFAULT_PAGE_SIZE)).option("--offset <number>", "Number of items to skip", "0").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_LIST_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
2171
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2172
|
+
const [error, result] = await catchError((async () => {
|
|
2173
|
+
const { api, organizationId } = await createApiClient(UserApi, {
|
|
2174
|
+
loginValidity: options.loginValidity
|
|
2175
|
+
});
|
|
2176
|
+
const orgId = organizationId;
|
|
2177
|
+
return await api.userGetUsers({
|
|
2178
|
+
partitionGlobalId: orgId,
|
|
2179
|
+
searchTerm: options.search,
|
|
2180
|
+
sortBy: options.sortBy,
|
|
2181
|
+
sortOrder: options.sortOrder,
|
|
2182
|
+
top: Number.parseInt(options.limit, 10),
|
|
2183
|
+
skip: Number.parseInt(options.offset || "0", 10)
|
|
2184
|
+
});
|
|
2185
|
+
})());
|
|
2186
|
+
if (error) {
|
|
2187
|
+
OutputFormatter.error({
|
|
2188
|
+
Result: RESULTS.Failure,
|
|
2189
|
+
Message: await extractErrorMessage(error),
|
|
2190
|
+
Instructions: INSTRUCTIONS7.list
|
|
2191
|
+
});
|
|
2192
|
+
processContext.exit(1);
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
OutputFormatter.success({
|
|
2196
|
+
Result: RESULTS.Success,
|
|
2197
|
+
Code: "UserList",
|
|
2198
|
+
Data: result.results ?? []
|
|
2199
|
+
});
|
|
2200
|
+
});
|
|
2201
|
+
users.command("get").description("Get user details by ID. Use 'users list' to retrieve the user ID.").argument("<user-id>", "User ID (UUID)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_GET_EXAMPLES).trackedAction(processContext, async (userId, options) => {
|
|
2202
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2203
|
+
const [error, user] = await catchError((async () => {
|
|
2204
|
+
const { api } = await createApiClient(UserApi, {
|
|
2205
|
+
loginValidity: options.loginValidity
|
|
2206
|
+
});
|
|
2207
|
+
return await api.userGetUser({ userId });
|
|
2208
|
+
})());
|
|
2209
|
+
if (error) {
|
|
2210
|
+
OutputFormatter.error({
|
|
2211
|
+
Result: RESULTS.Failure,
|
|
2212
|
+
Message: await extractErrorMessage(error),
|
|
2213
|
+
Instructions: INSTRUCTIONS7.get
|
|
2214
|
+
});
|
|
2215
|
+
processContext.exit(1);
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
OutputFormatter.success({
|
|
2219
|
+
Result: RESULTS.Success,
|
|
2220
|
+
Code: "UserDetails",
|
|
2221
|
+
Data: user
|
|
2222
|
+
});
|
|
2223
|
+
});
|
|
2224
|
+
users.command("create").description("Create a new user. Provide a username and email address.").argument("<username>", "Username for the new user").option("--organization <id>", "(deprecated, ignored) organization is derived from login").requiredOption("-e, --email <email>", "Email address").option("-n, --name <name>", "First name").option("--surname <surname>", "Last name").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_CREATE_EXAMPLES).trackedAction(processContext, async (username, options) => {
|
|
2225
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2226
|
+
const [error, result] = await catchError((async () => {
|
|
2227
|
+
const { api, organizationId } = await createApiClient(UserApi, {
|
|
2228
|
+
loginValidity: options.loginValidity
|
|
2229
|
+
});
|
|
2230
|
+
const orgId = organizationId;
|
|
2231
|
+
const createCommand = {
|
|
2232
|
+
partitionGlobalId: orgId,
|
|
2233
|
+
userName: username,
|
|
2234
|
+
email: options.email,
|
|
2235
|
+
name: options.name,
|
|
2236
|
+
surname: options.surname
|
|
2237
|
+
};
|
|
2238
|
+
return await api.userCreateUser({
|
|
2239
|
+
createUserCommand: createCommand
|
|
2240
|
+
});
|
|
2241
|
+
})());
|
|
2242
|
+
if (error) {
|
|
2243
|
+
OutputFormatter.error({
|
|
2244
|
+
Result: RESULTS.Failure,
|
|
2245
|
+
Message: await extractErrorMessage(error),
|
|
2246
|
+
Instructions: INSTRUCTIONS7.create
|
|
2247
|
+
});
|
|
2248
|
+
processContext.exit(1);
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
OutputFormatter.success({
|
|
2252
|
+
Result: RESULTS.Success,
|
|
2253
|
+
Code: "UserCreated",
|
|
2254
|
+
Data: result
|
|
2255
|
+
});
|
|
2256
|
+
});
|
|
2257
|
+
users.command("update").description("Update an existing user. Use 'users list' to retrieve the user ID.").argument("<user-id>", "User ID (UUID)").option("-e, --email <email>", "New email address").option("-n, --name <name>", "New first name").option("--surname <surname>", "New last name").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_UPDATE_EXAMPLES).trackedAction(processContext, async (userId, options) => {
|
|
2258
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2259
|
+
if (options.email === undefined && options.name === undefined && options.surname === undefined) {
|
|
2260
|
+
OutputFormatter.error({
|
|
2261
|
+
Result: RESULTS.Failure,
|
|
2262
|
+
Message: "No fields to update",
|
|
2263
|
+
Instructions: "Provide at least one of --email, --name, or --surname."
|
|
2264
|
+
});
|
|
2265
|
+
processContext.exit(1);
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
const updateCommand = {};
|
|
2269
|
+
if (options.email !== undefined)
|
|
2270
|
+
updateCommand.email = options.email;
|
|
2271
|
+
if (options.name !== undefined)
|
|
2272
|
+
updateCommand.name = options.name;
|
|
2273
|
+
if (options.surname !== undefined)
|
|
2274
|
+
updateCommand.surname = options.surname;
|
|
2275
|
+
const [error, result] = await catchError((async () => {
|
|
2276
|
+
const { api } = await createApiClient(UserApi, {
|
|
2277
|
+
loginValidity: options.loginValidity
|
|
2278
|
+
});
|
|
2279
|
+
return await api.userUpdate({
|
|
2280
|
+
userId,
|
|
2281
|
+
updateUserCommand: updateCommand
|
|
2282
|
+
});
|
|
2283
|
+
})());
|
|
2284
|
+
if (error) {
|
|
2285
|
+
OutputFormatter.error({
|
|
2286
|
+
Result: RESULTS.Failure,
|
|
2287
|
+
Message: await extractErrorMessage(error),
|
|
2288
|
+
Instructions: INSTRUCTIONS7.update
|
|
2289
|
+
});
|
|
2290
|
+
processContext.exit(1);
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
OutputFormatter.success({
|
|
2294
|
+
Result: RESULTS.Success,
|
|
2295
|
+
Code: "UserUpdated",
|
|
2296
|
+
Data: result
|
|
2297
|
+
});
|
|
2298
|
+
});
|
|
2299
|
+
users.command("delete").description("Delete a user by ID. Use 'users list' to retrieve the user ID.").argument("<user-id>", "User ID (UUID)").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_DELETE_EXAMPLES).trackedAction(processContext, async (userId, options) => {
|
|
2300
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2301
|
+
const [error] = await catchError((async () => {
|
|
2302
|
+
const { api } = await createApiClient(UserApi, {
|
|
2303
|
+
loginValidity: options.loginValidity
|
|
2304
|
+
});
|
|
2305
|
+
return await api.userDelete({ userId });
|
|
2306
|
+
})());
|
|
2307
|
+
if (error) {
|
|
2308
|
+
OutputFormatter.error({
|
|
2309
|
+
Result: RESULTS.Failure,
|
|
2310
|
+
Message: await extractErrorMessage(error),
|
|
2311
|
+
Instructions: INSTRUCTIONS7.delete
|
|
2312
|
+
});
|
|
2313
|
+
processContext.exit(1);
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
OutputFormatter.success({
|
|
2317
|
+
Result: RESULTS.Success,
|
|
2318
|
+
Code: "UserDeleted",
|
|
2319
|
+
Data: {
|
|
2320
|
+
Id: userId,
|
|
2321
|
+
Status: "Deleted successfully"
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
});
|
|
2325
|
+
users.command("invite").description("Invite users by email. Sends an invitation to join the organization.").requiredOption("-e, --email <emails>", "Comma-separated email addresses to invite").option("-n, --name <name>", "First name for the invited user").option("--surname <surname>", "Last name for the invited user").option("--login-validity <minutes>", "Override token validity (minutes)", (v) => Number.parseInt(v, 10)).examples(USERS_INVITE_EXAMPLES).trackedAction(processContext, async (options) => {
|
|
2326
|
+
warnDeprecatedOrganization4(options.organization);
|
|
2327
|
+
const [error, result] = await catchError((async () => {
|
|
2328
|
+
const { config } = await createIdentityConfig({
|
|
2329
|
+
loginValidity: options.loginValidity
|
|
2330
|
+
});
|
|
2331
|
+
const baseUrl = config.basePath.replace(/\/[^/]+\/identity_$/, "");
|
|
2332
|
+
const redirectUrl = `${baseUrl}/portal_/cloudrpa`;
|
|
2333
|
+
const api = new UserApi(config);
|
|
2334
|
+
const invitedUsers = options.email.split(",").map((e) => ({
|
|
2335
|
+
email: e.trim(),
|
|
2336
|
+
name: options.name,
|
|
2337
|
+
surname: options.surname,
|
|
2338
|
+
redirectUrl
|
|
2339
|
+
}));
|
|
2340
|
+
return await api.userInviteUsers({
|
|
2341
|
+
invitedUser: invitedUsers
|
|
2342
|
+
});
|
|
2343
|
+
})());
|
|
2344
|
+
if (error) {
|
|
2345
|
+
OutputFormatter.error({
|
|
2346
|
+
Result: RESULTS.Failure,
|
|
2347
|
+
Message: await extractErrorMessage(error),
|
|
2348
|
+
Instructions: INSTRUCTIONS7.invite
|
|
2349
|
+
});
|
|
2350
|
+
processContext.exit(1);
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
OutputFormatter.success({
|
|
2354
|
+
Result: RESULTS.Success,
|
|
2355
|
+
Code: "UsersInvited",
|
|
2356
|
+
Data: result
|
|
2357
|
+
});
|
|
2358
|
+
});
|
|
2359
|
+
};
|
|
2360
|
+
|
|
2361
|
+
// ../identity-commands/src/index.ts
|
|
2362
|
+
var registerCommands = async (program) => {
|
|
2363
|
+
registerExternalAppsCommand(program);
|
|
2364
|
+
registerGroupsCommand(program);
|
|
2365
|
+
registerPatCommand(program);
|
|
2366
|
+
registerRobotAccountsCommand(program);
|
|
2367
|
+
registerScopesCommand(program);
|
|
2368
|
+
registerSmtpCommand(program);
|
|
2369
|
+
registerUsersCommand(program);
|
|
2370
|
+
};
|
|
2371
|
+
export {
|
|
2372
|
+
registerCommands
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2375
|
+
//# debugId=BCA0BD64AB49604664756E2164756E21
|