opal-security 3.1.1-beta.e92fdf8 ā 3.1.1-beta.f1a5f49
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/README.md +44 -26
- package/lib/commands/login.js +6 -2
- package/lib/commands/request/create.d.ts +6 -0
- package/lib/commands/request/create.js +57 -14
- package/lib/commands/whoami.d.ts +8 -0
- package/lib/commands/whoami.js +34 -0
- package/lib/graphql/gql.d.ts +10 -0
- package/lib/graphql/gql.js +2 -0
- package/lib/graphql/graphql.d.ts +85 -0
- package/lib/graphql/graphql.js +539 -1
- package/lib/lib/apollo.js +3 -4
- package/lib/lib/credentials/index.d.ts +2 -1
- package/lib/lib/credentials/index.js +2 -1
- package/lib/lib/flags.js +1 -1
- package/lib/lib/requests.d.ts +21 -15
- package/lib/lib/requests.js +374 -75
- package/lib/utils/displays.js +8 -3
- package/oclif.manifest.json +99 -39
- package/package.json +1 -1
package/lib/lib/requests.d.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
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
|
-
|
|
4
|
+
import { type AppType, type ConnectionType, EntityType } from "../graphql/graphql";
|
|
5
|
+
type AppNode = {
|
|
6
|
+
appId: string;
|
|
5
7
|
appName: string;
|
|
8
|
+
appType?: AppType | ConnectionType;
|
|
6
9
|
assets: Record<string, AssetNode>;
|
|
7
|
-
}
|
|
8
|
-
|
|
10
|
+
};
|
|
11
|
+
type AssetNode = {
|
|
12
|
+
assetId: string;
|
|
9
13
|
assetName: string;
|
|
10
|
-
type:
|
|
14
|
+
type: EntityType;
|
|
11
15
|
roles?: Record<string, RoleNode>;
|
|
12
|
-
}
|
|
13
|
-
|
|
16
|
+
};
|
|
17
|
+
type RoleNode = {
|
|
18
|
+
roleId: string;
|
|
14
19
|
roleName: string;
|
|
15
|
-
}
|
|
20
|
+
};
|
|
16
21
|
export type RequestMap = Record<string, AppNode>;
|
|
17
|
-
|
|
22
|
+
type DurationOption = {
|
|
18
23
|
durationInMinutes: number;
|
|
19
24
|
label: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
25
|
+
};
|
|
26
|
+
type RequestDefaults = {
|
|
22
27
|
durationOptions?: DurationOption[];
|
|
23
28
|
recommendedDurationInMinutes?: number | null;
|
|
24
29
|
defaultDurationInMinutes?: number;
|
|
@@ -26,21 +31,22 @@ interface RequestDefaults {
|
|
|
26
31
|
requireSupportTicket?: boolean;
|
|
27
32
|
reasonOptional?: boolean;
|
|
28
33
|
requesterIsAdmin?: boolean;
|
|
29
|
-
}
|
|
30
|
-
export
|
|
34
|
+
};
|
|
35
|
+
export type RequestMetadata = {
|
|
31
36
|
requestMap: RequestMap;
|
|
32
37
|
requestDefaults: RequestDefaults;
|
|
33
38
|
durationLabel: string;
|
|
34
39
|
durationInMinutes?: number;
|
|
35
40
|
reason: string;
|
|
36
|
-
}
|
|
41
|
+
};
|
|
37
42
|
export declare function initEmptyRequestMetadata(): RequestMetadata;
|
|
38
43
|
export declare function selectRequestableItems(cmd: Command, client: ApolloClient<NormalizedCacheObject>, requestMap: RequestMap): Promise<void>;
|
|
39
|
-
export declare function chooseAssets(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, requestMap: RequestMap): Promise<void>;
|
|
40
|
-
export declare function chooseRoles(cmd: Command, client: ApolloClient<NormalizedCacheObject>, appId: string, assetId: string, requestMap: RequestMap): Promise<void>;
|
|
41
44
|
export declare function doneSelectingAssets(): Promise<boolean>;
|
|
42
45
|
export declare function setRequestDefaults(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
|
|
43
46
|
export declare function promptForReason(metadata: RequestMetadata): Promise<void>;
|
|
44
47
|
export declare function promptForExpiration(metadata: RequestMetadata): Promise<void>;
|
|
48
|
+
export declare function promptRequestSubmission(cmd: Command, metadata: RequestMetadata): Promise<boolean>;
|
|
45
49
|
export declare function submitFinalRequest(cmd: Command, client: ApolloClient<NormalizedCacheObject>, metadata: RequestMetadata): Promise<void>;
|
|
50
|
+
export declare function bypassRequestSelection(cmd: Command, client: ApolloClient<NormalizedCacheObject>, flagValue: string[], metadata: RequestMetadata): Promise<void>;
|
|
51
|
+
export declare function bypassDuration(cmd: Command, duration: number, metadata: RequestMetadata): void;
|
|
46
52
|
export {};
|
package/lib/lib/requests.js
CHANGED
|
@@ -2,19 +2,31 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initEmptyRequestMetadata = initEmptyRequestMetadata;
|
|
4
4
|
exports.selectRequestableItems = selectRequestableItems;
|
|
5
|
-
exports.chooseAssets = chooseAssets;
|
|
6
|
-
exports.chooseRoles = chooseRoles;
|
|
7
5
|
exports.doneSelectingAssets = doneSelectingAssets;
|
|
8
6
|
exports.setRequestDefaults = setRequestDefaults;
|
|
9
7
|
exports.promptForReason = promptForReason;
|
|
10
8
|
exports.promptForExpiration = promptForExpiration;
|
|
9
|
+
exports.promptRequestSubmission = promptRequestSubmission;
|
|
11
10
|
exports.submitFinalRequest = submitFinalRequest;
|
|
11
|
+
exports.bypassRequestSelection = bypassRequestSelection;
|
|
12
|
+
exports.bypassDuration = bypassDuration;
|
|
12
13
|
const chalk_1 = require("chalk");
|
|
13
|
-
const inquirer = require("inquirer");
|
|
14
14
|
const graphql_1 = require("../graphql");
|
|
15
|
+
const graphql_2 = require("../graphql/graphql");
|
|
15
16
|
const displays_1 = require("../utils/displays");
|
|
16
17
|
const config_1 = require("./config");
|
|
17
18
|
const { AutoComplete, Select, prompt, Form } = require("enquirer");
|
|
19
|
+
function entityTypeFromString(str) {
|
|
20
|
+
const capStr = str === null || str === void 0 ? void 0 : str.toLocaleUpperCase();
|
|
21
|
+
if (capStr === "RESOURCE") {
|
|
22
|
+
return graphql_2.EntityType.Resource;
|
|
23
|
+
}
|
|
24
|
+
if (capStr === "GROUP") {
|
|
25
|
+
return graphql_2.EntityType.Group;
|
|
26
|
+
}
|
|
27
|
+
// if type unknown, default to resource
|
|
28
|
+
return graphql_2.EntityType.Resource;
|
|
29
|
+
}
|
|
18
30
|
function initEmptyRequestMetadata() {
|
|
19
31
|
// Initialize with empty defaults
|
|
20
32
|
const requestDefaults = {
|
|
@@ -94,6 +106,7 @@ async function queryRequestableApps(cmd, client, input) {
|
|
|
94
106
|
value: {
|
|
95
107
|
id: edge.node.id,
|
|
96
108
|
name: edge.node.displayName,
|
|
109
|
+
type: type,
|
|
97
110
|
toString: () => edge.node.displayName,
|
|
98
111
|
},
|
|
99
112
|
};
|
|
@@ -166,7 +179,7 @@ async function queryRequestableAssets(cmd, client, appId, input) {
|
|
|
166
179
|
value: {
|
|
167
180
|
name: name || "",
|
|
168
181
|
id: id || "",
|
|
169
|
-
type: ((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)
|
|
182
|
+
type: entityTypeFromString(((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)),
|
|
170
183
|
},
|
|
171
184
|
};
|
|
172
185
|
});
|
|
@@ -485,39 +498,250 @@ async function createRequest(cmd, client, requestedResources, requestedGroups, r
|
|
|
485
498
|
}
|
|
486
499
|
}
|
|
487
500
|
}
|
|
501
|
+
const CATALOG_ITEM = (0, graphql_1.graphql)(`
|
|
502
|
+
query GetCatalogItem($uuid: UUID!) {
|
|
503
|
+
catalogItem(id: $uuid) {
|
|
504
|
+
__typename
|
|
505
|
+
... on Connection {
|
|
506
|
+
id
|
|
507
|
+
displayName
|
|
508
|
+
}
|
|
509
|
+
... on Resource {
|
|
510
|
+
id
|
|
511
|
+
displayName
|
|
512
|
+
connection {
|
|
513
|
+
id
|
|
514
|
+
displayName
|
|
515
|
+
}
|
|
516
|
+
accessLevels{
|
|
517
|
+
accessLevelName
|
|
518
|
+
accessLevelRemoteId
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
...on Group {
|
|
522
|
+
id
|
|
523
|
+
name
|
|
524
|
+
connection {
|
|
525
|
+
id
|
|
526
|
+
displayName
|
|
527
|
+
}
|
|
528
|
+
accessLevels{
|
|
529
|
+
accessLevelName
|
|
530
|
+
accessLevelRemoteId
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
... on UserFacingError {
|
|
534
|
+
message
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
`);
|
|
539
|
+
const ASSOCIATED_ITEMS_QUERY = (0, graphql_1.graphql)(`
|
|
540
|
+
query GetAssociatedItems($resourceId: ResourceId!, $searchQuery: String) {
|
|
541
|
+
resource(input: {
|
|
542
|
+
id: $resourceId
|
|
543
|
+
}) {
|
|
544
|
+
__typename
|
|
545
|
+
... on ResourceResult {
|
|
546
|
+
__typename
|
|
547
|
+
resource {
|
|
548
|
+
associatedItems(
|
|
549
|
+
first: 200
|
|
550
|
+
filters: {
|
|
551
|
+
searchQuery: {
|
|
552
|
+
contains: $searchQuery
|
|
553
|
+
}
|
|
554
|
+
access: REQUESTABLE
|
|
555
|
+
endUserVisible: true
|
|
556
|
+
entityType: {
|
|
557
|
+
in: [GROUP, RESOURCE]
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
) {
|
|
561
|
+
edges {
|
|
562
|
+
__typename
|
|
563
|
+
... on ResourceAssociatedItemEdge {
|
|
564
|
+
alias
|
|
565
|
+
node {
|
|
566
|
+
__typename
|
|
567
|
+
id
|
|
568
|
+
name
|
|
569
|
+
... on Resource {
|
|
570
|
+
accessLevels(
|
|
571
|
+
filters: {
|
|
572
|
+
skipRemoteAccessLevels: false # azure app roles are remote
|
|
573
|
+
}
|
|
574
|
+
) {
|
|
575
|
+
__typename
|
|
576
|
+
accessLevelName
|
|
577
|
+
accessLevelRemoteId
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
... on ResourceNotFoundError {
|
|
587
|
+
message
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
`);
|
|
592
|
+
async function queryAssociatedItems(cmd, client, id, input) {
|
|
593
|
+
var _a, _b;
|
|
594
|
+
try {
|
|
595
|
+
const resp = await client.query({
|
|
596
|
+
query: ASSOCIATED_ITEMS_QUERY,
|
|
597
|
+
variables: {
|
|
598
|
+
resourceId: id || "",
|
|
599
|
+
searchQuery: input || "",
|
|
600
|
+
},
|
|
601
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
602
|
+
});
|
|
603
|
+
switch (resp.data.resource.__typename) {
|
|
604
|
+
case "ResourceResult": {
|
|
605
|
+
const associatedItems = resp.data.resource.resource.associatedItems.edges.filter((edge) => edge.__typename === "ResourceAssociatedItemEdge");
|
|
606
|
+
const initial = [];
|
|
607
|
+
for (const edge of associatedItems) {
|
|
608
|
+
initial.push(...appRolesFromEdge(edge));
|
|
609
|
+
}
|
|
610
|
+
return initial;
|
|
611
|
+
}
|
|
612
|
+
case "ResourceNotFoundError":
|
|
613
|
+
cmd.log((_b = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.resource) === null || _b === void 0 ? void 0 : _b.message);
|
|
614
|
+
break;
|
|
615
|
+
default:
|
|
616
|
+
cmd.error(resp.error || "Unknown error occurred.");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
if (error instanceof Error || typeof error === "string") {
|
|
621
|
+
cmd.error(error);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
488
625
|
// Helper functions
|
|
626
|
+
const selectInstructions = chalk_1.default.dim("[āā] Navigate Ā· [Enter] Select Ā· Type to filter");
|
|
627
|
+
const multiSelectInstructions = chalk_1.default.dim("[āā] Navigate Ā· [Space] Select Ā· [Enter] Confirm Ā· Type to filter");
|
|
489
628
|
async function selectRequestableItems(cmd, client, requestMap) {
|
|
490
|
-
const
|
|
629
|
+
const initial = (await queryRequestableApps(cmd, client, "")) || [];
|
|
491
630
|
const appPrompt = new AutoComplete({
|
|
492
631
|
name: "App",
|
|
493
|
-
message: "Select an app
|
|
632
|
+
message: "Select an app",
|
|
633
|
+
hint: selectInstructions,
|
|
494
634
|
limit: 15,
|
|
495
|
-
choices:
|
|
635
|
+
choices: initial,
|
|
496
636
|
async suggest(input) {
|
|
497
637
|
const filteredChoices = await queryRequestableApps(cmd, client, input || "");
|
|
498
|
-
return filteredChoices ||
|
|
638
|
+
return filteredChoices || initial;
|
|
499
639
|
},
|
|
500
640
|
});
|
|
501
641
|
const App = await appPrompt.run();
|
|
502
642
|
// Set the app in the requestMap and call choose assets step
|
|
503
643
|
if (!(App.id in requestMap)) {
|
|
504
644
|
requestMap[App.id] = {
|
|
645
|
+
appId: App.id,
|
|
505
646
|
appName: App.name,
|
|
506
647
|
assets: {},
|
|
507
648
|
};
|
|
508
649
|
}
|
|
650
|
+
if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
|
|
651
|
+
await chooseOktaAzureRoles(cmd, client, App, requestMap);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
509
654
|
await chooseAssets(cmd, client, App.id, requestMap);
|
|
510
655
|
}
|
|
656
|
+
async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
657
|
+
const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
|
|
658
|
+
const rolePrompt = new AutoComplete({
|
|
659
|
+
name: "Roles",
|
|
660
|
+
message: `Select a role for ${app.name}:`,
|
|
661
|
+
hint: multiSelectInstructions,
|
|
662
|
+
limit: 15,
|
|
663
|
+
multiple: true,
|
|
664
|
+
async choices(input) {
|
|
665
|
+
if (!input)
|
|
666
|
+
return associatedItems;
|
|
667
|
+
const filteredChoices = await queryAssociatedItems(cmd, client, app.id, input);
|
|
668
|
+
return filteredChoices || associatedItems;
|
|
669
|
+
},
|
|
670
|
+
validate: (answer) => {
|
|
671
|
+
if (answer.length !== 1) {
|
|
672
|
+
return "Only one role is allowed to be requested for on Okta or Azure apps.";
|
|
673
|
+
}
|
|
674
|
+
return true;
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
const Roles = await rolePrompt.run();
|
|
678
|
+
const entry = requestMap[app.id];
|
|
679
|
+
for (const role of Roles) {
|
|
680
|
+
if (!(role.id in entry.assets)) {
|
|
681
|
+
entry.assets[role.id] = {
|
|
682
|
+
assetId: role.id,
|
|
683
|
+
assetName: role.name,
|
|
684
|
+
type: entityTypeFromString(role.type),
|
|
685
|
+
roles: {},
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function appRolesFromEdge(edge) {
|
|
691
|
+
var _a, _b, _c, _d;
|
|
692
|
+
switch (edge.node.__typename) {
|
|
693
|
+
case "Resource": {
|
|
694
|
+
if (edge.node.accessLevels && edge.node.accessLevels.length > 0) {
|
|
695
|
+
return edge.node.accessLevels.map((accessLevel) => ({
|
|
696
|
+
message: accessLevel.accessLevelName || "No Role (Direct access)",
|
|
697
|
+
value: {
|
|
698
|
+
id: edge.node.id + accessLevel.accessLevelRemoteId,
|
|
699
|
+
name: accessLevel.accessLevelName,
|
|
700
|
+
type: graphql_2.EntityType.Resource,
|
|
701
|
+
toString: () => accessLevel.accessLevelName,
|
|
702
|
+
},
|
|
703
|
+
}));
|
|
704
|
+
}
|
|
705
|
+
return [
|
|
706
|
+
{
|
|
707
|
+
message: (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name,
|
|
708
|
+
value: {
|
|
709
|
+
id: edge.node.id,
|
|
710
|
+
name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
|
|
711
|
+
type: graphql_2.EntityType.Resource,
|
|
712
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
];
|
|
716
|
+
}
|
|
717
|
+
case "Group":
|
|
718
|
+
return [
|
|
719
|
+
{
|
|
720
|
+
message: (_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name,
|
|
721
|
+
value: {
|
|
722
|
+
id: edge.node.id,
|
|
723
|
+
name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
|
|
724
|
+
type: graphql_2.EntityType.Group,
|
|
725
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
];
|
|
729
|
+
}
|
|
730
|
+
}
|
|
511
731
|
async function chooseAssets(cmd, client, appId, requestMap) {
|
|
512
|
-
const
|
|
732
|
+
const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
|
|
513
733
|
const assetPrompt = new AutoComplete({
|
|
514
734
|
name: "Assets",
|
|
515
735
|
message: "Select one or more assets:",
|
|
736
|
+
hint: multiSelectInstructions,
|
|
516
737
|
limit: 15,
|
|
517
738
|
multiple: true,
|
|
518
739
|
async choices(input) {
|
|
740
|
+
if (!input) {
|
|
741
|
+
return initial;
|
|
742
|
+
}
|
|
519
743
|
const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
|
|
520
|
-
return filteredChoices ||
|
|
744
|
+
return filteredChoices || initial;
|
|
521
745
|
},
|
|
522
746
|
validate: (answer) => {
|
|
523
747
|
if (answer.length < 1) {
|
|
@@ -534,6 +758,7 @@ async function chooseAssets(cmd, client, appId, requestMap) {
|
|
|
534
758
|
}
|
|
535
759
|
if (!(asset.id in entry.assets)) {
|
|
536
760
|
entry.assets[asset.id] = {
|
|
761
|
+
assetId: asset.id,
|
|
537
762
|
assetName: asset.name,
|
|
538
763
|
type: asset.type,
|
|
539
764
|
roles: {},
|
|
@@ -558,6 +783,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
|
558
783
|
const rolePrompt = new AutoComplete({
|
|
559
784
|
name: "Roles",
|
|
560
785
|
message: `Select one or more roles for ${assetEntry.assetName}:`,
|
|
786
|
+
hint: multiSelectInstructions,
|
|
561
787
|
limit: 15,
|
|
562
788
|
multiple: true,
|
|
563
789
|
choices: assetRoles,
|
|
@@ -574,6 +800,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
|
574
800
|
}
|
|
575
801
|
for (const role of roles) {
|
|
576
802
|
assetEntry.roles[role.id] = {
|
|
803
|
+
roleId: role.id,
|
|
577
804
|
roleName: role.name,
|
|
578
805
|
};
|
|
579
806
|
}
|
|
@@ -602,14 +829,14 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
602
829
|
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
603
830
|
for (const roleId of roleIds) {
|
|
604
831
|
switch (assetNode.type) {
|
|
605
|
-
case
|
|
832
|
+
case graphql_2.EntityType.Resource: {
|
|
606
833
|
requestedResources.push({
|
|
607
834
|
resourceId: assetId,
|
|
608
835
|
accessLevelRemoteId: roleId,
|
|
609
836
|
});
|
|
610
837
|
break;
|
|
611
838
|
}
|
|
612
|
-
case
|
|
839
|
+
case graphql_2.EntityType.Group: {
|
|
613
840
|
requestedGroups.push({
|
|
614
841
|
groupId: assetId,
|
|
615
842
|
accessLevelRemoteId: roleId,
|
|
@@ -647,7 +874,7 @@ async function promptForReason(metadata) {
|
|
|
647
874
|
const { reason } = await prompt([
|
|
648
875
|
{
|
|
649
876
|
name: "reason",
|
|
650
|
-
message: "
|
|
877
|
+
message: "Why do you need access?",
|
|
651
878
|
type: "input",
|
|
652
879
|
validate: (answer) => {
|
|
653
880
|
if (!metadata.requestDefaults.reasonOptional && answer.length < 1) {
|
|
@@ -769,77 +996,149 @@ async function setCustomDuration(metadata) {
|
|
|
769
996
|
label: durationLabel,
|
|
770
997
|
};
|
|
771
998
|
}
|
|
772
|
-
async function
|
|
773
|
-
|
|
999
|
+
async function promptRequestSubmission(cmd, metadata) {
|
|
1000
|
+
(0, displays_1.displayFinalRequestSummary)(cmd, metadata);
|
|
774
1001
|
const submitMessage = "ā
Yes, submit request";
|
|
775
1002
|
const cancelMessage = "ā No, cancel request";
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
}
|
|
1003
|
+
const prompt = new Select({
|
|
1004
|
+
name: "submitOrCancel",
|
|
1005
|
+
message: "Is this all you want to request?",
|
|
1006
|
+
choices: [submitMessage, cancelMessage],
|
|
1007
|
+
});
|
|
1008
|
+
const submitOrCancel = await prompt.run();
|
|
1009
|
+
return submitOrCancel === submitMessage;
|
|
1010
|
+
}
|
|
1011
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
1012
|
+
var _a, _b, _c, _d;
|
|
1013
|
+
// Build requested assets lists for the mutation
|
|
1014
|
+
const requestedResources = [];
|
|
1015
|
+
const requestedGroups = [];
|
|
1016
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
1017
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
1018
|
+
// Both extract the requestedResources and requestedGroups,
|
|
1019
|
+
// use different formats.
|
|
1020
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
1021
|
+
if (assetNode.roles) {
|
|
1022
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
1023
|
+
return roleId;
|
|
1024
|
+
});
|
|
1025
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
1026
|
+
for (const roleId of roleIds) {
|
|
1027
|
+
switch (assetNode.type) {
|
|
1028
|
+
case graphql_2.EntityType.Resource: {
|
|
1029
|
+
requestedResources.push({
|
|
1030
|
+
resourceId: assetId,
|
|
1031
|
+
accessLevel: {
|
|
1032
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
1033
|
+
accessLevelRemoteId: roleId,
|
|
1034
|
+
},
|
|
1035
|
+
});
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
case graphql_2.EntityType.Group: {
|
|
1039
|
+
requestedGroups.push({
|
|
1040
|
+
groupId: assetId,
|
|
1041
|
+
accessLevel: {
|
|
1042
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
1043
|
+
accessLevelRemoteId: roleId,
|
|
1044
|
+
},
|
|
1045
|
+
});
|
|
1046
|
+
break;
|
|
819
1047
|
}
|
|
820
1048
|
}
|
|
821
1049
|
}
|
|
822
1050
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
1054
|
+
// Build link to request
|
|
1055
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
1056
|
+
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
1057
|
+
cmd.log("\nš Your Access Request has been submitted!\n");
|
|
1058
|
+
cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
|
|
1059
|
+
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
1060
|
+
cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
|
|
1061
|
+
}
|
|
1062
|
+
const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
|
|
1063
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
1064
|
+
}
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
1068
|
+
var _a, _b;
|
|
1069
|
+
try {
|
|
1070
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
1071
|
+
for (const id of flagValue) {
|
|
1072
|
+
const [assetId, roleName] = id.split(":");
|
|
1073
|
+
const resp = await client.query({
|
|
1074
|
+
query: CATALOG_ITEM,
|
|
1075
|
+
variables: {
|
|
1076
|
+
uuid: assetId || "",
|
|
1077
|
+
},
|
|
1078
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
1079
|
+
});
|
|
1080
|
+
switch (resp.data.catalogItem.__typename) {
|
|
1081
|
+
case "Group":
|
|
1082
|
+
case "Resource": {
|
|
1083
|
+
const item = resp.data.catalogItem;
|
|
1084
|
+
const assetName = item.__typename === "Resource" ? item.displayName : item.name;
|
|
1085
|
+
const requestableRoles = (item.accessLevels || [])
|
|
1086
|
+
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
1087
|
+
.map((role) => ({
|
|
1088
|
+
id: role.accessLevelRemoteId,
|
|
1089
|
+
name: role.accessLevelName,
|
|
1090
|
+
}));
|
|
1091
|
+
const appId = ((_a = item.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
1092
|
+
if (!(appId in metadata.requestMap)) {
|
|
1093
|
+
metadata.requestMap[appId] = {
|
|
1094
|
+
appName: ((_b = item.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
|
|
1095
|
+
appId: appId,
|
|
1096
|
+
assets: {},
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
const assetEntry = metadata.requestMap[appId].assets[id];
|
|
1100
|
+
if (!assetEntry) {
|
|
1101
|
+
metadata.requestMap[appId].assets[assetId] = {
|
|
1102
|
+
assetId: assetId,
|
|
1103
|
+
assetName: assetName,
|
|
1104
|
+
type: entityTypeFromString(item.__typename),
|
|
1105
|
+
roles: {},
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
if (requestableRoles.length > 0 &&
|
|
1109
|
+
!(requestableRoles.length === 1 && requestableRoles[0].name === "")) {
|
|
1110
|
+
const selectedRole = requestableRoles.find((role) => role.name === roleName);
|
|
1111
|
+
if (selectedRole !== undefined) {
|
|
1112
|
+
if (!metadata.requestMap[appId].assets[assetId].roles) {
|
|
1113
|
+
metadata.requestMap[appId].assets[assetId].roles = {};
|
|
1114
|
+
}
|
|
1115
|
+
metadata.requestMap[appId].assets[assetId].roles[selectedRole.id] = {
|
|
1116
|
+
roleId: selectedRole.id,
|
|
1117
|
+
roleName: selectedRole.name,
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
cmd.error(`Access level specified does not match one of ${assetName}'s defined access levels: ${requestableRoles.map((role) => `"${role.name}"`)}`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
break;
|
|
831
1125
|
}
|
|
832
|
-
|
|
833
|
-
|
|
1126
|
+
default:
|
|
1127
|
+
cmd.error("Invalid asset id was passed in using the --id flag.");
|
|
834
1128
|
}
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
case cancelMessage: {
|
|
838
|
-
cmd.log("š« Access Request has been cancelled.");
|
|
839
|
-
return;
|
|
840
1129
|
}
|
|
841
|
-
|
|
842
|
-
|
|
1130
|
+
}
|
|
1131
|
+
catch (error) {
|
|
1132
|
+
if (error instanceof Error || typeof error === "string") {
|
|
1133
|
+
cmd.error(error);
|
|
843
1134
|
}
|
|
844
1135
|
}
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
1139
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
1140
|
+
if (maxDuration && duration > maxDuration) {
|
|
1141
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
|
|
1142
|
+
}
|
|
1143
|
+
metadata.durationInMinutes = duration;
|
|
845
1144
|
}
|
package/lib/utils/displays.js
CHANGED
|
@@ -28,12 +28,17 @@ function treeifyRequestMap(cmd, requestMap) {
|
|
|
28
28
|
for (const [_appId, appNode] of Object.entries(requestMap)) {
|
|
29
29
|
const assetsTree = {};
|
|
30
30
|
for (const [_assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
31
|
-
|
|
31
|
+
// If okta/azure asset with no role, change asset name
|
|
32
|
+
const assetName = assetNode.assetName || "No Role (Direct access)";
|
|
33
|
+
const assetKey = `${assetName} ${chalk_1.default.dim(`[${assetNode.type}]`)}`;
|
|
32
34
|
if (assetNode.roles !== undefined) {
|
|
33
35
|
assetsTree[assetKey] = {};
|
|
34
36
|
for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
+
const roleName = roleNode.roleName;
|
|
38
|
+
if (roleName !== "") {
|
|
39
|
+
const roleKey = `${roleName} ${chalk_1.default.dim("[Role]")}`;
|
|
40
|
+
assetsTree[assetKey][roleKey] = null;
|
|
41
|
+
}
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
else {
|