opal-security 3.1.0 → 3.1.1-beta.4a79f20

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,22 +1,42 @@
1
1
  import type { NormalizedCacheObject } from "@apollo/client/core";
2
2
  import type { ApolloClient } from "@apollo/client/core/ApolloClient";
3
3
  import type { Command } from "@oclif/core/lib/command";
4
- export interface AppNode {
4
+ interface AppNode {
5
5
  appName: string;
6
- assets: Map<string, AssetNode>;
6
+ assets: Record<string, AssetNode>;
7
7
  }
8
- export interface AssetNode {
8
+ interface AssetNode {
9
9
  assetName: string;
10
- roles?: Map<string, RoleNode>;
10
+ roles?: Record<string, RoleNode>;
11
11
  }
12
- export interface RoleNode {
12
+ interface RoleNode {
13
13
  roleName: string;
14
14
  }
15
- export type RequestMap = Map<string, AppNode>;
15
+ export type RequestMap = Record<string, AppNode>;
16
+ interface DurationOption {
17
+ durationInMinutes: number;
18
+ label: string;
19
+ }
20
+ interface RequestDefaults {
21
+ durationOptions?: DurationOption[];
22
+ recommendedDurationInMinutes?: number | null;
23
+ defaultDurationInMinutes?: number;
24
+ maxDurationInMinutes?: number | null;
25
+ requireSupportTicket?: boolean;
26
+ reasonOptional?: boolean;
27
+ requesterIsAdmin?: boolean;
28
+ }
29
+ export interface RequestMetadata {
30
+ requestMap: RequestMap;
31
+ requestDefaults: RequestDefaults;
32
+ }
33
+ export declare function createEmptyRequestMetadata(): RequestMetadata;
16
34
  export declare function selectRequestableItems(cmd: Command, client: ApolloClient<NormalizedCacheObject>, requestMap: RequestMap): Promise<void>;
17
35
  export declare function chooseAssets(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, requestMap: RequestMap): Promise<void>;
18
- export declare function chooseRoles(appId: string, assetId: string, requestMap: RequestMap): Promise<void>;
36
+ export declare function chooseRoles(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, assetId: string, requestMap: RequestMap): Promise<void>;
19
37
  export declare function doneSelectingAssets(): Promise<boolean>;
20
- export declare function promptForReason(): Promise<any>;
21
- export declare function promptForExpiration(): Promise<any>;
38
+ export declare function setRequestDefaults(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
39
+ export declare function promptForReason(metadata: RequestMetadata): Promise<any>;
40
+ export declare function promptForExpiration(metadata: RequestMetadata): Promise<any>;
22
41
  export declare function submitFinalRequest(cmd: Command): Promise<void>;
42
+ export {};
@@ -1,16 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEmptyRequestMetadata = createEmptyRequestMetadata;
3
4
  exports.selectRequestableItems = selectRequestableItems;
4
5
  exports.chooseAssets = chooseAssets;
5
6
  exports.chooseRoles = chooseRoles;
6
7
  exports.doneSelectingAssets = doneSelectingAssets;
8
+ exports.setRequestDefaults = setRequestDefaults;
7
9
  exports.promptForReason = promptForReason;
8
10
  exports.promptForExpiration = promptForExpiration;
9
11
  exports.submitFinalRequest = submitFinalRequest;
10
12
  const inquirer = require("inquirer");
11
13
  const graphql_1 = require("../graphql");
12
14
  inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
15
+ function createEmptyRequestMetadata() {
16
+ // Initialize with empty defaults
17
+ const requestDefaults = {
18
+ durationOptions: [],
19
+ recommendedDurationInMinutes: undefined,
20
+ defaultDurationInMinutes: undefined,
21
+ maxDurationInMinutes: undefined,
22
+ requireSupportTicket: false,
23
+ reasonOptional: false,
24
+ requesterIsAdmin: false,
25
+ };
26
+ // Initialize with empty map
27
+ const requestMap = {};
28
+ return {
29
+ requestMap,
30
+ requestDefaults,
31
+ };
32
+ }
13
33
  // Queries and Mutations
34
+ // TODO: add pagination ability from CLI. (Load more...) option
14
35
  const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
15
36
  query GetRequestableAppsQuery($searchQuery: String) {
16
37
  appsV2(
@@ -40,39 +61,6 @@ const GET_REQUESTABLE_APPS_QUERY = (0, graphql_1.graphql)(`
40
61
  }
41
62
  }
42
63
  `);
43
- const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
44
- query PaginatedEntityDropdown(
45
- $id: UUID!
46
- $searchQuery: String
47
- ) {
48
- app(id: $id) {
49
- __typename
50
- ... on App {
51
- id
52
- items(
53
- input: {
54
- access: REQUESTABLE
55
- searchQuery: $searchQuery
56
- includeOnlyRequestable: true
57
- }
58
- ) {
59
- items {
60
- key
61
- resource {
62
- id
63
- name
64
- }
65
- group {
66
- id
67
- name
68
- }
69
- }
70
- cursor
71
- }
72
- }
73
- }
74
- }
75
- `);
76
64
  async function queryRequestableApps(cmd, client, input) {
77
65
  var _a, _b;
78
66
  try {
@@ -107,8 +95,44 @@ async function queryRequestableApps(cmd, client, input) {
107
95
  }
108
96
  }
109
97
  }
98
+ const GET_ASSETS_QUERY = (0, graphql_1.graphql)(`
99
+ query PaginatedEntityDropdown(
100
+ $id: UUID!
101
+ $searchQuery: String
102
+ ) {
103
+ app(id: $id) {
104
+ __typename
105
+ ... on App {
106
+ id
107
+ items(
108
+ input: {
109
+ access: REQUESTABLE
110
+ searchQuery: $searchQuery
111
+ includeOnlyRequestable: true
112
+ }
113
+ ) {
114
+ items {
115
+ key
116
+ resource {
117
+ id
118
+ name
119
+ }
120
+ group {
121
+ id
122
+ name
123
+ }
124
+ }
125
+ cursor
126
+ }
127
+ }
128
+ ... on AppNotFoundError {
129
+ message
130
+ }
131
+ }
132
+ }
133
+ `);
110
134
  async function queryRequestableAssets(cmd, client, appId, input) {
111
- var _a, _b, _c, _d;
135
+ var _a, _b, _c, _d, _e, _f;
112
136
  try {
113
137
  const resp = await client.query({
114
138
  query: GET_ASSETS_QUERY,
@@ -137,10 +161,10 @@ async function queryRequestableAssets(cmd, client, appId, input) {
137
161
  };
138
162
  });
139
163
  case "AppNotFoundError":
140
- x = cmd.error("App not found");
164
+ x = cmd.error((_f = (_e = resp.data) === null || _e === void 0 ? void 0 : _e.app) === null || _f === void 0 ? void 0 : _f.message);
141
165
  break;
142
166
  default:
143
- cmd.error("Unknown error occurred.");
167
+ cmd.error(resp.error || "Unknown error occurred.");
144
168
  }
145
169
  }
146
170
  catch (error) {
@@ -149,6 +173,104 @@ async function queryRequestableAssets(cmd, client, appId, input) {
149
173
  }
150
174
  }
151
175
  }
176
+ const RESOURCE_ROLES_QUERY = (0, graphql_1.graphql)(`
177
+ query ResourceAccessLevels($resourceId: ResourceId!) {
178
+ accessLevels(input: {
179
+ resourceId: $resourceId,
180
+ onlyMine: false,
181
+ }) {
182
+ __typename
183
+ ... on ResourceAccessLevelsResult {
184
+ accessLevels {
185
+ __typename
186
+ ... on ResourceAccessLevel {
187
+ accessLevelName
188
+ accessLevelRemoteId
189
+ }
190
+ }
191
+ }
192
+ ... on ResourceNotFoundError {
193
+ message
194
+ }
195
+ }
196
+ }
197
+ `);
198
+ async function queryResourceRoles(cmd, client, resourceId) {
199
+ var _a, _b, _c, _d, _e;
200
+ try {
201
+ const resp = await client.query({
202
+ query: RESOURCE_ROLES_QUERY,
203
+ variables: {
204
+ resourceId: resourceId,
205
+ },
206
+ fetchPolicy: "network-only", // to avoid caching
207
+ });
208
+ // no fall through doesn't consider process.exit();
209
+ let x;
210
+ switch (resp.data.accessLevels.__typename) {
211
+ case "ResourceAccessLevelsResult":
212
+ return (_c = (_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.accessLevels) === null || _b === void 0 ? void 0 : _b.accessLevels) === null || _c === void 0 ? void 0 : _c.map((role) => {
213
+ return {
214
+ name: role.accessLevelName,
215
+ value: {
216
+ name: role.accessLevelName,
217
+ id: role.accessLevelRemoteId,
218
+ },
219
+ };
220
+ });
221
+ case "ResourceNotFoundError":
222
+ x = cmd.error((_e = (_d = resp.data) === null || _d === void 0 ? void 0 : _d.accessLevels) === null || _e === void 0 ? void 0 : _e.message);
223
+ break;
224
+ default:
225
+ cmd.error(resp.error || "Unknown error occurred.");
226
+ }
227
+ }
228
+ catch (error) {
229
+ if (error instanceof Error || typeof error === "string") {
230
+ cmd.error(error);
231
+ }
232
+ }
233
+ }
234
+ const REQUEST_DEFAULTS_QUERY = (0, graphql_1.graphql)(`
235
+ query RequestDefaults(
236
+ $requestedResources: [RequestConfigurationResourceInput!]!
237
+ $requestedGroups: [RequestConfigurationGroupInput!]!
238
+ ) {
239
+ requestDefaults(input: {
240
+ requestedResources: $requestedResources,
241
+ requestedGroups: $requestedGroups,
242
+ }
243
+ ) {
244
+ durationOptions {
245
+ durationInMinutes
246
+ label
247
+ }
248
+ recommendedDurationInMinutes
249
+ defaultDurationInMinutes
250
+ maxDurationInMinutes
251
+ requireSupportTicket
252
+ reasonOptional
253
+ requesterIsAdmin
254
+ }
255
+ }`);
256
+ async function queryRequestDefaults(cmd, client, requestedResources, requestedGroups) {
257
+ try {
258
+ const resp = await client.query({
259
+ query: REQUEST_DEFAULTS_QUERY,
260
+ variables: {
261
+ requestedResources: requestedResources,
262
+ requestedGroups: requestedGroups,
263
+ },
264
+ fetchPolicy: "network-only", // to avoid caching
265
+ });
266
+ return resp.data.requestDefaults;
267
+ }
268
+ catch (error) {
269
+ if (error instanceof Error || typeof error === "string") {
270
+ cmd.error(error);
271
+ }
272
+ }
273
+ }
152
274
  // Helper functions
153
275
  async function selectRequestableItems(cmd, client, requestMap) {
154
276
  const { App } = await inquirer.prompt([
@@ -164,11 +286,11 @@ async function selectRequestableItems(cmd, client, requestMap) {
164
286
  },
165
287
  ]);
166
288
  // Set the app in the requestMap and call choose assets step
167
- if (!requestMap.has(App.id)) {
168
- requestMap.set(App.id, {
289
+ if (!(App.id in requestMap)) {
290
+ requestMap[App.id] = {
169
291
  appName: App.name,
170
- assets: new Map(),
171
- });
292
+ assets: {},
293
+ };
172
294
  }
173
295
  await chooseAssets(cmd, client, App.id, requestMap);
174
296
  }
@@ -187,37 +309,52 @@ async function chooseAssets(cmd, client, appId, requestMap) {
187
309
  return true;
188
310
  },
189
311
  });
190
- const entry = requestMap.get(appId);
312
+ const entry = requestMap[appId];
191
313
  for (const asset of Assets) {
192
314
  if (entry === undefined) {
193
315
  throw new Error(`App ${appId} not found in requestMap`);
194
316
  }
195
- if (!entry.assets.has(asset.id)) {
196
- entry.assets.set(asset.id, {
317
+ if (!(asset.id in entry.assets)) {
318
+ entry.assets[asset.id] = {
197
319
  assetName: asset.name,
198
- roles: new Map(),
199
- });
320
+ roles: {},
321
+ };
200
322
  }
201
- await chooseRoles(appId, asset.id, requestMap);
323
+ await chooseRoles(cmd, client, appId, asset.id, requestMap);
202
324
  }
203
325
  }
204
- async function chooseRoles(appId, assetId, requestMap) {
326
+ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
205
327
  var _a;
328
+ const resourceRoles = (_a = (await queryResourceRoles(cmd, client, assetId))) !== null && _a !== void 0 ? _a : [];
329
+ if (resourceRoles !== undefined &&
330
+ (resourceRoles.length === 0 ||
331
+ (resourceRoles.length === 1 && resourceRoles[0].name === ""))) {
332
+ return;
333
+ }
206
334
  const { roles } = await inquirer.prompt({
207
335
  name: "roles",
208
336
  type: "checkbox",
209
337
  message: `Select one or more roles for ${assetId}:`,
210
- choices: ["push", "pull", "triage", "admin"],
338
+ choices: resourceRoles,
339
+ validate: (answer) => {
340
+ if ((resourceRoles === null || resourceRoles === void 0 ? void 0 : resourceRoles.length) > 1 && answer.length < 1) {
341
+ return "You must select at least one role.";
342
+ }
343
+ return true;
344
+ },
211
345
  });
212
- const entry = requestMap.get(appId);
213
- const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets.get(assetId);
346
+ const entry = requestMap[appId];
347
+ const assetEntry = entry === null || entry === void 0 ? void 0 : entry.assets[assetId];
214
348
  if (entry === undefined || assetEntry === undefined) {
215
349
  throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
216
350
  }
351
+ if (!assetEntry.roles) {
352
+ assetEntry.roles = {};
353
+ }
217
354
  for (const role of roles) {
218
- (_a = assetEntry.roles) === null || _a === void 0 ? void 0 : _a.set(role, {
219
- roleName: role,
220
- });
355
+ assetEntry.roles[role.id] = {
356
+ roleName: role.name,
357
+ };
221
358
  }
222
359
  }
223
360
  async function doneSelectingAssets() {
@@ -233,22 +370,84 @@ async function doneSelectingAssets() {
233
370
  ]);
234
371
  return submitOrAdd === submitMessage;
235
372
  }
236
- async function promptForReason() {
373
+ async function setRequestDefaults(cmd, client, metadata) {
374
+ const requestMap = metadata.requestMap;
375
+ const requestedResources = [];
376
+ const requestedGroups = [];
377
+ for (const appNode of Object.values(requestMap)) {
378
+ for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
379
+ if (assetNode.roles !== undefined) {
380
+ for (const roleId of Object.keys(assetNode.roles)) {
381
+ requestedResources.push({
382
+ resourceId: assetId,
383
+ accessLevelRemoteId: roleId,
384
+ });
385
+ }
386
+ }
387
+ }
388
+ }
389
+ try {
390
+ const requestDefaults = await queryRequestDefaults(cmd, client, requestedResources, requestedGroups);
391
+ if ((requestDefaults === null || requestDefaults === void 0 ? void 0 : requestDefaults.__typename) === "RequestDefaults") {
392
+ metadata.requestDefaults.durationOptions =
393
+ requestDefaults.durationOptions;
394
+ metadata.requestDefaults.recommendedDurationInMinutes =
395
+ requestDefaults.recommendedDurationInMinutes;
396
+ metadata.requestDefaults.defaultDurationInMinutes =
397
+ requestDefaults.defaultDurationInMinutes;
398
+ metadata.requestDefaults.maxDurationInMinutes =
399
+ requestDefaults.maxDurationInMinutes;
400
+ metadata.requestDefaults.requireSupportTicket =
401
+ requestDefaults.requireSupportTicket;
402
+ metadata.requestDefaults.reasonOptional = requestDefaults.reasonOptional;
403
+ metadata.requestDefaults.requesterIsAdmin =
404
+ requestDefaults.requesterIsAdmin;
405
+ }
406
+ }
407
+ catch (_a) {
408
+ cmd.error("Error fetching request defaults.");
409
+ }
410
+ }
411
+ async function promptForReason(metadata) {
237
412
  return await inquirer.prompt([
238
413
  {
239
414
  name: "reason",
240
415
  message: "I need access to this because...",
241
416
  type: "input",
417
+ validate: (answer) => {
418
+ if (metadata.requestDefaults.reasonOptional && answer.length < 1) {
419
+ return "A reason for requesting these assets is required.";
420
+ }
421
+ return true;
422
+ },
242
423
  },
243
424
  ]);
244
425
  }
245
- async function promptForExpiration() {
426
+ async function promptForExpiration(metadata) {
427
+ var _a, _b;
428
+ const durations = ((_b = (_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.durationOptions) === null || _b === void 0 ? void 0 : _b.map((option) => {
429
+ return {
430
+ name: option.durationInMinutes ===
431
+ metadata.requestDefaults.recommendedDurationInMinutes
432
+ ? `${option.label} (Recommended)`
433
+ : option.label,
434
+ value: {
435
+ label: option.label,
436
+ durationInMinutes: option.durationInMinutes,
437
+ },
438
+ };
439
+ })) || [];
440
+ // TODO: Sort durations by minutes
441
+ // durations = durations.sort(
442
+ // durations.filter((option) => option.value.durationInMinutes),
443
+ // );
246
444
  return await inquirer.prompt([
247
445
  {
248
446
  name: "expiration",
249
447
  message: "When should access expire?",
250
448
  type: "list",
251
- choices: ["1 hour", "1 day", "7 days", "30 days", "1 year", "Indefinite"],
449
+ choices: durations,
450
+ pageSize: 15,
252
451
  },
253
452
  ]);
254
453
  }
@@ -1,5 +1,8 @@
1
+ import type { ApolloQueryResult } from "@apollo/client";
1
2
  import type { Command } from "@oclif/core/lib/command";
3
+ import type { GetRequestQuery } from "../graphql/graphql";
2
4
  import type { RequestMap } from "../lib/requests";
3
5
  export declare function headerMessage(cmd: Command): void;
4
6
  export declare function treeifyRequestMap(requestMap: RequestMap): string;
5
7
  export declare function displayFinalRequestSummary(cmd: Command, requestMap: RequestMap, reason: string, expiration: string): void;
8
+ export declare function displayRequestDetails(cmd: Command, requestResp: ApolloQueryResult<GetRequestQuery>): void;
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.headerMessage = headerMessage;
4
4
  exports.treeifyRequestMap = treeifyRequestMap;
5
5
  exports.displayFinalRequestSummary = displayFinalRequestSummary;
6
+ exports.displayRequestDetails = displayRequestDetails;
7
+ const chalk_1 = require("chalk");
8
+ const prettyjson_1 = require("prettyjson");
6
9
  const treeify = require("object-treeify");
7
10
  const Table = require("cli-table3");
8
11
  const tableStyle = {
@@ -32,18 +35,18 @@ function treeifyRequestMap(requestMap) {
32
35
  const requestTree = {};
33
36
  // Create a tree structure from the requestMap
34
37
  // Iterate over apps
35
- for (const [_appId, appNode] of requestMap.entries()) {
38
+ for (const [_appId, appNode] of Object.entries(requestMap)) {
36
39
  const appKey = `🔧${appNode.appName}`;
37
40
  requestTree[appKey] = {}; // Initialize the app key
38
41
  // Iterate over assets
39
- for (const [_assetId, assetNode] of appNode.assets.entries()) {
42
+ for (const [_assetId, assetNode] of Object.entries(appNode.assets)) {
40
43
  const assetKey = `📦${assetNode.assetName}`;
41
44
  if (assetNode.roles !== undefined) {
42
45
  // If no roles were previously selected
43
46
  requestTree[appKey][assetKey] = {}; // Initialize the asset key
44
47
  // Iterate over roles
45
- for (const [roleName, _] of assetNode.roles.entries()) {
46
- requestTree[appKey][assetKey][roleName] = null; // Initialize the role key
48
+ for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
49
+ requestTree[appKey][assetKey][roleNode.roleName] = null; // Initialize the role key
47
50
  }
48
51
  }
49
52
  else {
@@ -63,3 +66,64 @@ function displayFinalRequestSummary(cmd, requestMap, reason, expiration) {
63
66
  table.push(["Requested Assets", requestedAssets], ["Reason", reason], ["Expiration", expiration]);
64
67
  cmd.log(table.toString());
65
68
  }
69
+ function displayRequestDetails(cmd, requestResp) {
70
+ var _a, _b;
71
+ switch (requestResp.data.request.__typename) {
72
+ case "RequestResult": {
73
+ cmd.log(`REQUEST ${requestResp.data.request.request.id}`);
74
+ const status = requestResp.data.request.request.status;
75
+ // If status is "PENDING" or "APPROVED", display it
76
+ // If status is "DENIED", display it in red
77
+ if (status === "PENDING") {
78
+ cmd.log(`Status: ${chalk_1.default.yellowBright(status)}`);
79
+ }
80
+ else if (status === "APPROVED") {
81
+ cmd.log(`Status: ${chalk_1.default.greenBright(status)}`);
82
+ }
83
+ else if (status === "DENIED") {
84
+ cmd.log(`Status: ${chalk_1.default.redBright(status)}`);
85
+ }
86
+ else if (status === "CANCELED") {
87
+ cmd.log(`Status: ${chalk_1.default.redBright(status)}`);
88
+ }
89
+ // Request users "Requester: <requester> -> Target: <targetUser>"
90
+ const requester = (_a = requestResp.data.request.request.requester) === null || _a === void 0 ? void 0 : _a.displayName;
91
+ const targetUser = (_b = requestResp.data.request.request.targetUser) === null || _b === void 0 ? void 0 : _b.displayName;
92
+ if (requester && targetUser) {
93
+ cmd.log(`Requester: ${requester} -> Target: ${targetUser}`);
94
+ }
95
+ const durationInMinutes = requestResp.data.request.request.durationInMinutes;
96
+ if (durationInMinutes) {
97
+ const days = Math.floor(durationInMinutes / 1440);
98
+ const remainingMinutes = durationInMinutes % 1440;
99
+ const hours = Math.floor(remainingMinutes / 60);
100
+ const minutes = remainingMinutes % 60;
101
+ let durationStr = "";
102
+ if (days > 0)
103
+ durationStr += `${days}d`;
104
+ if (hours > 0)
105
+ durationStr += `${hours}h`;
106
+ if (minutes > 0)
107
+ durationStr += `${minutes}m`;
108
+ if (durationStr === "")
109
+ durationStr = `${durationInMinutes}m`;
110
+ durationStr += ` (${durationInMinutes}m)`;
111
+ cmd.log(`Duration: ${durationStr}`);
112
+ }
113
+ const reason = requestResp.data.request.request.reason;
114
+ if (reason) {
115
+ cmd.log(`Reason: "${reason}"`);
116
+ }
117
+ const requestedResources = requestResp.data.request.request.requestedResources;
118
+ const requestedGroups = requestResp.data.request.request.requestedGroups;
119
+ if (requestedResources && requestedResources.length > 0) {
120
+ cmd.log("Requested Resources:");
121
+ cmd.log((0, prettyjson_1.render)(requestedResources));
122
+ }
123
+ if (requestedGroups && requestedGroups.length > 0) {
124
+ cmd.log("Requested Groups:");
125
+ cmd.log((0, prettyjson_1.render)(requestedGroups));
126
+ }
127
+ }
128
+ }
129
+ }
@@ -620,7 +620,30 @@
620
620
  "aliases": [],
621
621
  "args": {},
622
622
  "description": "Lists access requests",
623
- "flags": {},
623
+ "flags": {
624
+ "help": {
625
+ "char": "h",
626
+ "description": "Show CLI help.",
627
+ "name": "help",
628
+ "allowNo": false,
629
+ "type": "boolean"
630
+ },
631
+ "id": {
632
+ "char": "i",
633
+ "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
634
+ "name": "id",
635
+ "hasDynamicHelp": false,
636
+ "multiple": false,
637
+ "type": "option"
638
+ },
639
+ "verbose": {
640
+ "char": "v",
641
+ "description": "Enable verbose output",
642
+ "name": "verbose",
643
+ "allowNo": false,
644
+ "type": "boolean"
645
+ }
646
+ },
624
647
  "hasDynamicHelp": false,
625
648
  "hidden": true,
626
649
  "hiddenAliases": [],
@@ -639,12 +662,33 @@
639
662
  ]
640
663
  },
641
664
  "request:list": {
642
- "aliases": [
643
- "request:ls"
644
- ],
665
+ "aliases": [],
645
666
  "args": {},
646
- "description": "Lists access requests",
647
- "flags": {},
667
+ "description": "Lists your recent outgoing access requests. \n--pageSize flag sets number of requests to be returned. Defaults to 10 requests. \n--showPendingOnly flag will show only pending requests. Defaults to false.",
668
+ "flags": {
669
+ "help": {
670
+ "char": "h",
671
+ "description": "Show CLI help.",
672
+ "name": "help",
673
+ "allowNo": false,
674
+ "type": "boolean"
675
+ },
676
+ "id": {
677
+ "char": "i",
678
+ "description": "The Opal ID of the resource. You can find this from the URL, e.g. https://opal.dev/resources/[ID]",
679
+ "name": "id",
680
+ "hasDynamicHelp": false,
681
+ "multiple": false,
682
+ "type": "option"
683
+ },
684
+ "pageSize": {
685
+ "description": "Sets number of requests to be returned. Defaults to 10 requests.",
686
+ "name": "pageSize",
687
+ "hasDynamicHelp": false,
688
+ "multiple": false,
689
+ "type": "option"
690
+ }
691
+ },
648
692
  "hasDynamicHelp": false,
649
693
  "hidden": true,
650
694
  "hiddenAliases": [],
@@ -909,5 +953,5 @@
909
953
  ]
910
954
  }
911
955
  },
912
- "version": "3.1.0"
956
+ "version": "3.1.1-beta.4a79f20"
913
957
  }
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.0",
4
+ "version": "3.1.1-beta.4a79f20",
5
5
  "author": "Stephen Cobbe",
6
6
  "bin": {
7
7
  "opal": "./bin/run"
@@ -39,6 +39,7 @@
39
39
  "@types/lodash": "^4.14.169",
40
40
  "@types/node": "^22.14.0",
41
41
  "@types/prettyjson": "0.0.29",
42
+ "@types/react": "^19.1.4",
42
43
  "@types/semver": "^7.3.8",
43
44
  "better-npm-audit": "^3.7.3",
44
45
  "get-graphql-schema": "^2.1.2",