opal-security 3.1.1-beta.778ef29 → 3.1.1-beta.7e1cc21

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.
@@ -1,5 +1,10 @@
1
+ import type { ApolloQueryResult } from "@apollo/client";
1
2
  import type { Command } from "@oclif/core/lib/command";
2
- import type { RequestMap } from "../lib/requests";
3
+ import type { GetRequestQuery, GetRequestsQuery } from "../graphql/graphql";
4
+ import { type RequestMap, type RequestMetadata } from "../lib/requests";
3
5
  export declare function headerMessage(cmd: Command): void;
4
- export declare function treeifyRequestMap(requestMap: RequestMap): string;
5
- export declare function displayFinalRequestSummary(cmd: Command, requestMap: RequestMap, reason: string, expiration: string): void;
6
+ export declare function treeifyRequestMap(cmd: Command, requestMap: RequestMap): void;
7
+ export declare function displayFinalRequestSummary(cmd: Command, metadata: RequestMetadata): void;
8
+ export declare function getStyledStatus(status: string): string;
9
+ export declare function displayRequestDetails(cmd: Command, requestResp: ApolloQueryResult<GetRequestQuery>): void;
10
+ export declare function displayRequestListTable(cmd: Command, requestResp: ApolloQueryResult<GetRequestsQuery>): void;
@@ -3,63 +3,217 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.headerMessage = headerMessage;
4
4
  exports.treeifyRequestMap = treeifyRequestMap;
5
5
  exports.displayFinalRequestSummary = displayFinalRequestSummary;
6
- const treeify = require("object-treeify");
6
+ exports.getStyledStatus = getStyledStatus;
7
+ exports.displayRequestDetails = displayRequestDetails;
8
+ exports.displayRequestListTable = displayRequestListTable;
9
+ const chalk_1 = require("chalk");
10
+ const requests_1 = require("../lib/requests");
7
11
  const Table = require("cli-table3");
8
- const tableStyle = {
9
- top: "═",
10
- "top-mid": "╤",
11
- "top-left": "╔",
12
- "top-right": "╗",
13
- bottom: "═",
14
- "bottom-mid": "╧",
15
- "bottom-left": "╚",
16
- "bottom-right": "╝",
17
- left: "║",
18
- "left-mid": "╟",
19
- mid: "─",
20
- "mid-mid": "┼",
21
- right: "║",
22
- "right-mid": "╢",
23
- middle: "│",
24
- };
12
+ const treeify = require("object-treeify").default;
25
13
  function headerMessage(cmd) {
26
14
  console.clear();
27
- cmd.log("============================================================");
15
+ cmd.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
28
16
  cmd.log("Opal Access Request ✏️");
29
17
  cmd.log("Press Ctrl+C to cancel at any time.\n");
30
18
  }
31
- function treeifyRequestMap(requestMap) {
32
- const requestTree = {};
33
- // Create a tree structure from the requestMap
34
- // Iterate over apps
35
- for (const appNode of requestMap.values()) {
36
- const appKey = `🔧${appNode.appName}`;
37
- requestTree[appKey] = {}; // Initialize the app key
38
- // Iterate over assets
39
- for (const assetNode of appNode.assets.values()) {
40
- const assetKey = `📦${assetNode.assetName}`;
19
+ function treeifyRequestMap(cmd, requestMap) {
20
+ // Configuration options for treeify
21
+ const options = {
22
+ joined: true,
23
+ spacerNoNeighbour: " ",
24
+ spacerNeighbour: "│ ",
25
+ keyNoNeighbour: "└── ",
26
+ keyNeighbour: "├── ",
27
+ separator: "",
28
+ };
29
+ for (const [_appId, appNode] of Object.entries(requestMap)) {
30
+ const assetsTree = {};
31
+ for (const [_assetId, assetNode] of Object.entries(appNode.assets)) {
32
+ // If okta/azure asset with no role, change asset name
33
+ const assetName = assetNode.assetName || "No Role (Direct access)";
34
+ const assetKey = `${assetName} ${chalk_1.default.dim(`[${requests_1.DISPLAY_LABELS[assetNode.type]}]`)}`;
41
35
  if (assetNode.roles !== undefined) {
42
- // If no roles were previously selected
43
- requestTree[appKey][assetKey] = {}; // Initialize the asset key
44
- // Iterate over roles
45
- for (const roleNode of assetNode.roles.values()) {
46
- requestTree[appKey][assetKey][roleNode.roleName] = null; // Initialize the role key
36
+ assetsTree[assetKey] = {};
37
+ for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
38
+ const roleName = roleNode.roleName;
39
+ if (roleName !== "") {
40
+ const roleKey = `${roleName} ${chalk_1.default.dim("[Role]")}`;
41
+ assetsTree[assetKey][roleKey] = null;
42
+ }
47
43
  }
48
44
  }
49
45
  else {
50
- requestTree[appKey][assetKey] = null;
46
+ assetsTree[assetKey] = null;
51
47
  }
52
48
  }
49
+ // Render tree for this app's assets
50
+ const assetsTreeString = treeify(assetsTree, options);
51
+ // Print App title first (without tree lines)
52
+ cmd.log(`${chalk_1.default.bold(appNode.appName)} ${chalk_1.default.dim("[App]")}`);
53
+ // Print its assets/roles indented underneath
54
+ cmd.log(assetsTreeString);
53
55
  }
54
- return String(treeify(requestTree));
56
+ cmd.log();
55
57
  }
56
- function displayFinalRequestSummary(cmd, requestMap, reason, expiration) {
57
- headerMessage(cmd);
58
- cmd.log("Final Summary of Request\n");
59
- const requestedAssets = treeifyRequestMap(requestMap);
60
- const table = new Table({
61
- chars: tableStyle,
62
- });
63
- table.push(["Requested Assets", requestedAssets], ["Reason", reason], ["Expiration", expiration]);
64
- cmd.log(table.toString());
58
+ function displayFinalRequestSummary(cmd, metadata) {
59
+ console.clear();
60
+ cmd.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
61
+ cmd.log("Final Summary of Request ✏️");
62
+ cmd.log("Press Ctrl+C to cancel at any time.\n");
63
+ cmd.log();
64
+ treeifyRequestMap(cmd, metadata.requestMap);
65
+ const durationInMinutes = metadata.durationInMinutes;
66
+ cmd.log(`Duration: ${durationInMinutes ? formatDuration(durationInMinutes) : "Permanent"}`);
67
+ const reason = metadata.reason;
68
+ if (reason) {
69
+ cmd.log(`Reason: "${chalk_1.default.italic(reason)}"`);
70
+ }
71
+ cmd.log();
72
+ }
73
+ function getStyledStatus(status) {
74
+ switch (status) {
75
+ case "PENDING": {
76
+ return `${chalk_1.default.bold("Status:")} ${chalk_1.default.blueBright(status)}`;
77
+ }
78
+ case "APPROVED": {
79
+ return `${chalk_1.default.bold("Status:")} ${chalk_1.default.greenBright(status)}`;
80
+ }
81
+ case "DENIED": {
82
+ return `${chalk_1.default.bold("Status:")} ${chalk_1.default.redBright(status)}`;
83
+ }
84
+ case "CANCELED": {
85
+ return `${chalk_1.default.bold("Status:")} ${chalk_1.default.redBright(status)}`;
86
+ }
87
+ default: {
88
+ return `${chalk_1.default.bold("Status:")} ${chalk_1.default.gray(status)}`;
89
+ }
90
+ }
91
+ }
92
+ function displayRequestDetails(cmd, requestResp) {
93
+ var _a, _b, _c, _d, _e, _f;
94
+ switch (requestResp.data.request.__typename) {
95
+ case "RequestResult": {
96
+ cmd.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
97
+ cmd.log(`Request Details ${chalk_1.default.cyan(requestResp.data.request.request.id)}`);
98
+ cmd.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
99
+ const status = requestResp.data.request.request.status;
100
+ cmd.log(getStyledStatus(status));
101
+ // Request users "Requested by: <requester> -> Requested for: <targetUser>"
102
+ const requester = (_a = requestResp.data.request.request.requester) === null || _a === void 0 ? void 0 : _a.displayName;
103
+ const targetUser = (_b = requestResp.data.request.request.targetUser) === null || _b === void 0 ? void 0 : _b.displayName;
104
+ if (requester && targetUser) {
105
+ cmd.log(`${chalk_1.default.bold("Requested by:")} ${requester} ${chalk_1.default.gray("->")} ${chalk_1.default.bold("Requested for:")} ${targetUser}`);
106
+ }
107
+ const durationInMinutes = requestResp.data.request.request.durationInMinutes;
108
+ cmd.log(`${chalk_1.default.bold("Duration:")} ${durationInMinutes ? formatDuration(durationInMinutes) : "Permanent"}`);
109
+ const reason = requestResp.data.request.request.reason;
110
+ if (reason) {
111
+ cmd.log(`${chalk_1.default.bold("Reason:")} "${chalk_1.default.italic(reason)}"`);
112
+ }
113
+ // Requested resources
114
+ const requestedResources = (_d = (_c = requestResp.data.request.request.requestedResources) === null || _c === void 0 ? void 0 : _c.map((resource) => {
115
+ var _a, _b, _c;
116
+ if (((_a = resource.resource) === null || _a === void 0 ? void 0 : _a.__typename) === "Resource") {
117
+ return formatAssetName((_b = resource.resource) === null || _b === void 0 ? void 0 : _b.displayName, ((_c = resource.accessLevel) === null || _c === void 0 ? void 0 : _c.accessLevelName) || "");
118
+ }
119
+ })) !== null && _d !== void 0 ? _d : [];
120
+ const requestedGroups = (_f = (_e = requestResp.data.request.request.requestedGroups) === null || _e === void 0 ? void 0 : _e.map((group) => {
121
+ var _a, _b, _c;
122
+ if (((_a = group.group) === null || _a === void 0 ? void 0 : _a.__typename) === "Group") {
123
+ return formatAssetName((_b = group.group) === null || _b === void 0 ? void 0 : _b.name, ((_c = group.accessLevel) === null || _c === void 0 ? void 0 : _c.accessLevelName) || "");
124
+ }
125
+ })) !== null && _f !== void 0 ? _f : [];
126
+ const requestedItems = [...requestedResources, ...requestedGroups].join(", ");
127
+ if (requestedItems) {
128
+ cmd.log(`${chalk_1.default.bold("Requested Items:")} ${chalk_1.default.cyan(requestedItems)}`);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ function displayRequestListTable(cmd, requestResp) {
134
+ var _a, _b, _c, _d, _e;
135
+ switch (requestResp.data.requests.__typename) {
136
+ case "RequestsResult": {
137
+ const requests = requestResp.data.requests.requests;
138
+ if (requests && requests.length > 0) {
139
+ const table = new Table({
140
+ head: [
141
+ "Request ID",
142
+ "Status",
143
+ "For",
144
+ "Duration",
145
+ "Requested Items",
146
+ "Reason",
147
+ ],
148
+ colWidths: [null, null, 20, null, 30, 20],
149
+ wordWrap: true,
150
+ wrapOnWordBoundary: false,
151
+ });
152
+ for (const request of requests) {
153
+ const targetUser = (_a = request.targetUser) === null || _a === void 0 ? void 0 : _a.displayName;
154
+ const reason = request.reason;
155
+ const status = request.status;
156
+ const formattedDuration = request.durationInMinutes
157
+ ? formatDuration(request.durationInMinutes)
158
+ : "Permanent";
159
+ const requestedResources = (_c = (_b = request.requestedResources) === null || _b === void 0 ? void 0 : _b.map((resource) => {
160
+ var _a, _b, _c;
161
+ if (((_a = resource.resource) === null || _a === void 0 ? void 0 : _a.__typename) === "Resource") {
162
+ return formatAssetName((_b = resource.resource) === null || _b === void 0 ? void 0 : _b.displayName, ((_c = resource.accessLevel) === null || _c === void 0 ? void 0 : _c.accessLevelName) || "");
163
+ }
164
+ })) !== null && _c !== void 0 ? _c : [];
165
+ const requestedGroups = (_e = (_d = request.requestedGroups) === null || _d === void 0 ? void 0 : _d.map((group) => {
166
+ var _a, _b, _c;
167
+ if (((_a = group.group) === null || _a === void 0 ? void 0 : _a.__typename) === "Group") {
168
+ return formatAssetName((_b = group.group) === null || _b === void 0 ? void 0 : _b.name, ((_c = group.accessLevel) === null || _c === void 0 ? void 0 : _c.accessLevelName) || "");
169
+ }
170
+ })) !== null && _e !== void 0 ? _e : [];
171
+ const requestedItems = [
172
+ ...requestedResources,
173
+ ...requestedGroups,
174
+ ].join(", ");
175
+ table.push([
176
+ request.id,
177
+ status,
178
+ targetUser,
179
+ formattedDuration,
180
+ requestedItems,
181
+ reason,
182
+ ]);
183
+ }
184
+ cmd.log(table.toString());
185
+ }
186
+ else {
187
+ cmd.log("No requests found.");
188
+ }
189
+ return;
190
+ }
191
+ default: {
192
+ cmd.log("No requests found.");
193
+ }
194
+ }
195
+ }
196
+ function formatDuration(durationInMinutes) {
197
+ const days = Math.floor(durationInMinutes / 1440);
198
+ const remainingMinutes = durationInMinutes % 1440;
199
+ const hours = Math.floor(remainingMinutes / 60);
200
+ const minutes = remainingMinutes % 60;
201
+ let durationStr = "";
202
+ if (days > 0)
203
+ durationStr += `${days}d`;
204
+ if (hours > 0)
205
+ durationStr += `${hours}h`;
206
+ if (minutes > 0)
207
+ durationStr += `${minutes}m`;
208
+ if (durationStr === "")
209
+ durationStr = `${durationInMinutes}m`;
210
+ durationStr += ` (${durationInMinutes}m)`;
211
+ return durationStr;
212
+ }
213
+ function formatAssetName(assetName, roleName) {
214
+ let str = `${assetName}`;
215
+ if (roleName) {
216
+ str += ` (${roleName})`;
217
+ }
218
+ return str;
65
219
  }
@@ -316,6 +316,34 @@
316
316
  "set-url.js"
317
317
  ]
318
318
  },
319
+ "whoami": {
320
+ "aliases": [],
321
+ "args": {},
322
+ "description": "Describes current url set, organization name, and logged in user if applicabled.",
323
+ "flags": {
324
+ "help": {
325
+ "char": "h",
326
+ "description": "Show CLI help.",
327
+ "name": "help",
328
+ "allowNo": false,
329
+ "type": "boolean"
330
+ }
331
+ },
332
+ "hasDynamicHelp": false,
333
+ "hiddenAliases": [],
334
+ "id": "whoami",
335
+ "pluginAlias": "opal-security",
336
+ "pluginName": "opal-security",
337
+ "pluginType": "core",
338
+ "strict": true,
339
+ "enableJsonFlag": false,
340
+ "isESM": false,
341
+ "relativePath": [
342
+ "lib",
343
+ "commands",
344
+ "whoami.js"
345
+ ]
346
+ },
319
347
  "aws:identity": {
320
348
  "aliases": [],
321
349
  "args": {},
@@ -365,7 +393,7 @@
365
393
  },
366
394
  "id": {
367
395
  "char": "i",
368
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
396
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
369
397
  "name": "id",
370
398
  "hasDynamicHelp": false,
371
399
  "multiple": false,
@@ -388,14 +416,14 @@
388
416
  "get.js"
389
417
  ]
390
418
  },
391
- "iam-roles:start": {
419
+ "kube-roles:start": {
392
420
  "aliases": [],
393
421
  "args": {},
394
- "description": "Starts a session to assume an IAM role.",
422
+ "description": "Starts a session to assume a Kubernetes cluster IAM role.",
395
423
  "examples": [
396
- "opal iam-roles:start",
397
- "opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398",
398
- "opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --profileName \"custom-profile\""
424
+ "opal kube-roles:start",
425
+ "opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398",
426
+ "opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId \"arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role\""
399
427
  ],
400
428
  "flags": {
401
429
  "help": {
@@ -407,12 +435,20 @@
407
435
  },
408
436
  "id": {
409
437
  "char": "i",
410
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
438
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
411
439
  "name": "id",
412
440
  "hasDynamicHelp": false,
413
441
  "multiple": false,
414
442
  "type": "option"
415
443
  },
444
+ "accessLevelRemoteId": {
445
+ "char": "a",
446
+ "description": "The remote ID of the access level with which to access the resource.",
447
+ "name": "accessLevelRemoteId",
448
+ "hasDynamicHelp": false,
449
+ "multiple": false,
450
+ "type": "option"
451
+ },
416
452
  "sessionId": {
417
453
  "char": "s",
418
454
  "description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
@@ -427,18 +463,11 @@
427
463
  "name": "refresh",
428
464
  "allowNo": false,
429
465
  "type": "boolean"
430
- },
431
- "profileName": {
432
- "description": "Uses a custom AWS profile name for the IAM role. Default value is the role's name.",
433
- "name": "profileName",
434
- "hasDynamicHelp": false,
435
- "multiple": false,
436
- "type": "option"
437
466
  }
438
467
  },
439
468
  "hasDynamicHelp": false,
440
469
  "hiddenAliases": [],
441
- "id": "iam-roles:start",
470
+ "id": "kube-roles:start",
442
471
  "pluginAlias": "opal-security",
443
472
  "pluginName": "opal-security",
444
473
  "pluginType": "core",
@@ -448,18 +477,18 @@
448
477
  "relativePath": [
449
478
  "lib",
450
479
  "commands",
451
- "iam-roles",
480
+ "kube-roles",
452
481
  "start.js"
453
482
  ]
454
483
  },
455
- "kube-roles:start": {
484
+ "iam-roles:start": {
456
485
  "aliases": [],
457
486
  "args": {},
458
- "description": "Starts a session to assume a Kubernetes cluster IAM role.",
487
+ "description": "Starts a session to assume an IAM role.",
459
488
  "examples": [
460
- "opal kube-roles:start",
461
- "opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398",
462
- "opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId \"arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role\""
489
+ "opal iam-roles:start",
490
+ "opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398",
491
+ "opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --profileName \"custom-profile\""
463
492
  ],
464
493
  "flags": {
465
494
  "help": {
@@ -471,20 +500,12 @@
471
500
  },
472
501
  "id": {
473
502
  "char": "i",
474
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
503
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
475
504
  "name": "id",
476
505
  "hasDynamicHelp": false,
477
506
  "multiple": false,
478
507
  "type": "option"
479
508
  },
480
- "accessLevelRemoteId": {
481
- "char": "a",
482
- "description": "The remote ID of the access level with which to access the resource.",
483
- "name": "accessLevelRemoteId",
484
- "hasDynamicHelp": false,
485
- "multiple": false,
486
- "type": "option"
487
- },
488
509
  "sessionId": {
489
510
  "char": "s",
490
511
  "description": "The Opal ID of the session to connect to. Uses an existing session that was created via the web flow.",
@@ -499,11 +520,18 @@
499
520
  "name": "refresh",
500
521
  "allowNo": false,
501
522
  "type": "boolean"
523
+ },
524
+ "profileName": {
525
+ "description": "Uses a custom AWS profile name for the IAM role. Default value is the role's name.",
526
+ "name": "profileName",
527
+ "hasDynamicHelp": false,
528
+ "multiple": false,
529
+ "type": "option"
502
530
  }
503
531
  },
504
532
  "hasDynamicHelp": false,
505
533
  "hiddenAliases": [],
506
- "id": "kube-roles:start",
534
+ "id": "iam-roles:start",
507
535
  "pluginAlias": "opal-security",
508
536
  "pluginName": "opal-security",
509
537
  "pluginType": "core",
@@ -513,7 +541,7 @@
513
541
  "relativePath": [
514
542
  "lib",
515
543
  "commands",
516
- "kube-roles",
544
+ "iam-roles",
517
545
  "start.js"
518
546
  ]
519
547
  },
@@ -537,7 +565,7 @@
537
565
  },
538
566
  "id": {
539
567
  "char": "i",
540
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
568
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
541
569
  "name": "id",
542
570
  "hasDynamicHelp": false,
543
571
  "multiple": false,
@@ -597,10 +625,41 @@
597
625
  "request:create": {
598
626
  "aliases": [],
599
627
  "args": {},
600
- "description": "Opens an Opal access request",
601
- "flags": {},
628
+ "description": "Creates an Opal access request via an interactive form",
629
+ "flags": {
630
+ "help": {
631
+ "char": "h",
632
+ "description": "Show CLI help.",
633
+ "name": "help",
634
+ "allowNo": false,
635
+ "type": "boolean"
636
+ },
637
+ "assets": {
638
+ "char": "a",
639
+ "description": "The ids of the assets (resource, group) to request access to. Append a role ID using a colon if needed, e.g. `--assets 123:456`. \n If not provided, an interactive selection flow will be available to select assets to request.",
640
+ "name": "assets",
641
+ "hasDynamicHelp": false,
642
+ "multiple": true,
643
+ "type": "option"
644
+ },
645
+ "reason": {
646
+ "char": "r",
647
+ "description": "The reason for the request, contained in quotes. If not provided, you will be prompted.",
648
+ "name": "reason",
649
+ "hasDynamicHelp": false,
650
+ "multiple": false,
651
+ "type": "option"
652
+ },
653
+ "duration": {
654
+ "char": "d",
655
+ "description": "The duration of access for the request in minutes. If not provided, you will be prompted.",
656
+ "name": "duration",
657
+ "hasDynamicHelp": false,
658
+ "multiple": false,
659
+ "type": "option"
660
+ }
661
+ },
602
662
  "hasDynamicHelp": false,
603
- "hidden": true,
604
663
  "hiddenAliases": [],
605
664
  "id": "request:create",
606
665
  "pluginAlias": "opal-security",
@@ -620,9 +679,35 @@
620
679
  "aliases": [],
621
680
  "args": {},
622
681
  "description": "Lists access requests",
623
- "flags": {},
682
+ "examples": [
683
+ "opal request get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4",
684
+ "opal request get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4 --verbose"
685
+ ],
686
+ "flags": {
687
+ "help": {
688
+ "char": "h",
689
+ "description": "Show CLI help.",
690
+ "name": "help",
691
+ "allowNo": false,
692
+ "type": "boolean"
693
+ },
694
+ "id": {
695
+ "char": "i",
696
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
697
+ "name": "id",
698
+ "hasDynamicHelp": false,
699
+ "multiple": false,
700
+ "type": "option"
701
+ },
702
+ "verbose": {
703
+ "char": "v",
704
+ "description": "Enable verbose output, prints full response in JSON format. Defaults to false.",
705
+ "name": "verbose",
706
+ "allowNo": false,
707
+ "type": "boolean"
708
+ }
709
+ },
624
710
  "hasDynamicHelp": false,
625
- "hidden": true,
626
711
  "hiddenAliases": [],
627
712
  "id": "request:get",
628
713
  "pluginAlias": "opal-security",
@@ -641,10 +726,46 @@
641
726
  "request:list": {
642
727
  "aliases": [],
643
728
  "args": {},
644
- "description": "Lists access requests",
645
- "flags": {},
729
+ "description": "Lists your n recent outgoing access requests",
730
+ "examples": [
731
+ "opal request list --n 5",
732
+ "opal request list --n 5 --pending",
733
+ "opal request list --n 5 --verbose",
734
+ "opal request list --n 5 --pending --verbose"
735
+ ],
736
+ "flags": {
737
+ "help": {
738
+ "char": "h",
739
+ "description": "Show CLI help.",
740
+ "name": "help",
741
+ "allowNo": false,
742
+ "type": "boolean"
743
+ },
744
+ "n": {
745
+ "char": "n",
746
+ "description": "Defines number of requests to be returned. 1 <= n <= 100.",
747
+ "name": "n",
748
+ "default": 10,
749
+ "hasDynamicHelp": false,
750
+ "multiple": false,
751
+ "type": "option"
752
+ },
753
+ "pending": {
754
+ "char": "p",
755
+ "description": "Show only pending requests. Defaults to false.",
756
+ "name": "pending",
757
+ "allowNo": false,
758
+ "type": "boolean"
759
+ },
760
+ "verbose": {
761
+ "char": "v",
762
+ "description": "Enable verbose output, prints full response in JSON format. Defaults to false.",
763
+ "name": "verbose",
764
+ "allowNo": false,
765
+ "type": "boolean"
766
+ }
767
+ },
646
768
  "hasDynamicHelp": false,
647
- "hidden": true,
648
769
  "hiddenAliases": [],
649
770
  "id": "request:list",
650
771
  "pluginAlias": "opal-security",
@@ -677,7 +798,7 @@
677
798
  },
678
799
  "id": {
679
800
  "char": "i",
680
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
801
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
681
802
  "name": "id",
682
803
  "hasDynamicHelp": false,
683
804
  "multiple": false,
@@ -744,7 +865,7 @@
744
865
  },
745
866
  "id": {
746
867
  "char": "i",
747
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
868
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
748
869
  "name": "id",
749
870
  "hasDynamicHelp": false,
750
871
  "multiple": false,
@@ -819,7 +940,7 @@
819
940
  },
820
941
  "id": {
821
942
  "char": "i",
822
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
943
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
823
944
  "name": "id",
824
945
  "hasDynamicHelp": false,
825
946
  "multiple": false,
@@ -868,7 +989,7 @@
868
989
  },
869
990
  "id": {
870
991
  "char": "i",
871
- "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
992
+ "description": "The Opal ID of the asset. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
872
993
  "name": "id",
873
994
  "hasDynamicHelp": false,
874
995
  "multiple": false,
@@ -907,5 +1028,5 @@
907
1028
  ]
908
1029
  }
909
1030
  },
910
- "version": "3.1.1-beta.778ef29"
1031
+ "version": "3.1.1-beta.7e1cc21"
911
1032
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opal-security",
3
3
  "description": "Opal allows you to centrally manage access to all of your sensitive systems.",
4
- "version": "3.1.1-beta.778ef29",
4
+ "version": "3.1.1-beta.7e1cc21",
5
5
  "author": "Stephen Cobbe",
6
6
  "bin": {
7
7
  "opal": "./bin/run"
@@ -16,6 +16,7 @@
16
16
  "argon2": "^0.40.1",
17
17
  "chalk": "^2.4.2",
18
18
  "cli-table3": "^0.6.5",
19
+ "enquirer": "^2.4.1",
19
20
  "graphql": "^15.5.0",
20
21
  "inquirer": "^8.2.6",
21
22
  "inquirer-autocomplete-prompt": "^2.0.1",
@@ -23,6 +24,7 @@
23
24
  "lodash": "^4.17.21",
24
25
  "moment": "^2.30.1",
25
26
  "node-fetch": "^2.6.7",
27
+ "object-treeify": "^5.0.1",
26
28
  "open": "^8.0.4",
27
29
  "openid-client": "^5.6.5",
28
30
  "prettyjson": "^1.2.1",
@@ -39,6 +41,7 @@
39
41
  "@types/lodash": "^4.14.169",
40
42
  "@types/node": "^22.14.0",
41
43
  "@types/prettyjson": "0.0.29",
44
+ "@types/react": "^19.1.4",
42
45
  "@types/semver": "^7.3.8",
43
46
  "better-npm-audit": "^3.7.3",
44
47
  "get-graphql-schema": "^2.1.2",