opal-security 3.1.1-beta.8a533e4 ā 3.1.1-beta.981ec0b
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 +26 -26
- package/lib/commands/request/create.d.ts +6 -0
- package/lib/commands/request/create.js +50 -13
- 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/flags.js +1 -1
- package/lib/lib/requests.d.ts +21 -15
- package/lib/lib/requests.js +372 -61
- package/lib/utils/displays.js +8 -3
- package/oclif.manifest.json +43 -11
- 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<void>;
|
|
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
14
|
const inquirer = require("inquirer");
|
|
14
15
|
const graphql_1 = require("../graphql");
|
|
16
|
+
const graphql_2 = require("../graphql/graphql");
|
|
15
17
|
const displays_1 = require("../utils/displays");
|
|
16
18
|
const config_1 = require("./config");
|
|
17
19
|
const { AutoComplete, Select, prompt, Form } = require("enquirer");
|
|
20
|
+
function entityTypeFromString(str) {
|
|
21
|
+
if (str === "Resource") {
|
|
22
|
+
return graphql_2.EntityType.Resource;
|
|
23
|
+
}
|
|
24
|
+
if (str === "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
|
};
|
|
@@ -485,39 +498,245 @@ 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
|
|
489
626
|
async function selectRequestableItems(cmd, client, requestMap) {
|
|
490
|
-
const
|
|
627
|
+
const initial = (await queryRequestableApps(cmd, client, "")) || [];
|
|
491
628
|
const appPrompt = new AutoComplete({
|
|
492
629
|
name: "App",
|
|
493
630
|
message: "Select an app:",
|
|
494
631
|
limit: 15,
|
|
495
|
-
choices:
|
|
632
|
+
choices: initial,
|
|
496
633
|
async suggest(input) {
|
|
497
634
|
const filteredChoices = await queryRequestableApps(cmd, client, input || "");
|
|
498
|
-
return filteredChoices ||
|
|
635
|
+
return filteredChoices || initial;
|
|
499
636
|
},
|
|
500
637
|
});
|
|
501
638
|
const App = await appPrompt.run();
|
|
502
639
|
// Set the app in the requestMap and call choose assets step
|
|
503
640
|
if (!(App.id in requestMap)) {
|
|
504
641
|
requestMap[App.id] = {
|
|
642
|
+
appId: App.id,
|
|
505
643
|
appName: App.name,
|
|
506
644
|
assets: {},
|
|
507
645
|
};
|
|
508
646
|
}
|
|
647
|
+
if (App.type === "OKTA_APP" || App.type === "AZURE_ENTERPRISE_APP") {
|
|
648
|
+
await chooseOktaAzureRoles(cmd, client, App, requestMap);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
509
651
|
await chooseAssets(cmd, client, App.id, requestMap);
|
|
510
652
|
}
|
|
653
|
+
async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
|
|
654
|
+
const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
|
|
655
|
+
const rolePrompt = new AutoComplete({
|
|
656
|
+
name: "Roles",
|
|
657
|
+
message: `Select a role for ${app.name}:`,
|
|
658
|
+
limit: 15,
|
|
659
|
+
multiple: true,
|
|
660
|
+
async choices(input) {
|
|
661
|
+
if (!input)
|
|
662
|
+
return associatedItems;
|
|
663
|
+
const filteredChoices = await queryAssociatedItems(cmd, client, app.id, input);
|
|
664
|
+
return filteredChoices || associatedItems;
|
|
665
|
+
},
|
|
666
|
+
validate: (answer) => {
|
|
667
|
+
if (answer.length !== 1) {
|
|
668
|
+
return "You must select only one item.";
|
|
669
|
+
}
|
|
670
|
+
return true;
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
const Roles = await rolePrompt.run();
|
|
674
|
+
const entry = requestMap[app.id];
|
|
675
|
+
for (const role of Roles) {
|
|
676
|
+
if (!(role.id in entry.assets)) {
|
|
677
|
+
entry.assets[role.id] = {
|
|
678
|
+
assetId: role.id,
|
|
679
|
+
assetName: role.name,
|
|
680
|
+
type: entityTypeFromString(role.type),
|
|
681
|
+
roles: {},
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function appRolesFromEdge(edge) {
|
|
687
|
+
var _a, _b, _c, _d;
|
|
688
|
+
switch (edge.node.__typename) {
|
|
689
|
+
case "Resource": {
|
|
690
|
+
if (edge.node.accessLevels && edge.node.accessLevels.length > 0) {
|
|
691
|
+
return edge.node.accessLevels.map((accessLevel) => ({
|
|
692
|
+
message: accessLevel.accessLevelName || "No Role (Direct access)",
|
|
693
|
+
value: {
|
|
694
|
+
id: edge.node.id + accessLevel.accessLevelRemoteId,
|
|
695
|
+
name: accessLevel.accessLevelName,
|
|
696
|
+
type: graphql_2.EntityType.Resource,
|
|
697
|
+
toString: () => accessLevel.accessLevelName,
|
|
698
|
+
},
|
|
699
|
+
}));
|
|
700
|
+
}
|
|
701
|
+
return [
|
|
702
|
+
{
|
|
703
|
+
message: (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name,
|
|
704
|
+
value: {
|
|
705
|
+
id: edge.node.id,
|
|
706
|
+
name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
|
|
707
|
+
type: graphql_2.EntityType.Resource,
|
|
708
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
];
|
|
712
|
+
}
|
|
713
|
+
case "Group":
|
|
714
|
+
return [
|
|
715
|
+
{
|
|
716
|
+
message: (_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name,
|
|
717
|
+
value: {
|
|
718
|
+
id: edge.node.id,
|
|
719
|
+
name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
|
|
720
|
+
type: graphql_2.EntityType.Group,
|
|
721
|
+
toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
];
|
|
725
|
+
}
|
|
726
|
+
}
|
|
511
727
|
async function chooseAssets(cmd, client, appId, requestMap) {
|
|
512
|
-
const
|
|
728
|
+
const initial = (await queryRequestableAssets(cmd, client, appId, "")) || [];
|
|
513
729
|
const assetPrompt = new AutoComplete({
|
|
514
730
|
name: "Assets",
|
|
515
731
|
message: "Select one or more assets:",
|
|
516
732
|
limit: 15,
|
|
517
733
|
multiple: true,
|
|
518
734
|
async choices(input) {
|
|
735
|
+
if (!input) {
|
|
736
|
+
return initial;
|
|
737
|
+
}
|
|
519
738
|
const filteredChoices = await queryRequestableAssets(cmd, client, appId, input);
|
|
520
|
-
return filteredChoices ||
|
|
739
|
+
return filteredChoices || initial;
|
|
521
740
|
},
|
|
522
741
|
validate: (answer) => {
|
|
523
742
|
if (answer.length < 1) {
|
|
@@ -534,6 +753,7 @@ async function chooseAssets(cmd, client, appId, requestMap) {
|
|
|
534
753
|
}
|
|
535
754
|
if (!(asset.id in entry.assets)) {
|
|
536
755
|
entry.assets[asset.id] = {
|
|
756
|
+
assetId: asset.id,
|
|
537
757
|
assetName: asset.name,
|
|
538
758
|
type: asset.type,
|
|
539
759
|
roles: {},
|
|
@@ -574,6 +794,7 @@ async function chooseRoles(cmd, client, appId, assetId, requestMap) {
|
|
|
574
794
|
}
|
|
575
795
|
for (const role of roles) {
|
|
576
796
|
assetEntry.roles[role.id] = {
|
|
797
|
+
roleId: role.id,
|
|
577
798
|
roleName: role.name,
|
|
578
799
|
};
|
|
579
800
|
}
|
|
@@ -602,14 +823,14 @@ async function setRequestDefaults(cmd, client, metadata) {
|
|
|
602
823
|
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
603
824
|
for (const roleId of roleIds) {
|
|
604
825
|
switch (assetNode.type) {
|
|
605
|
-
case
|
|
826
|
+
case graphql_2.EntityType.Resource: {
|
|
606
827
|
requestedResources.push({
|
|
607
828
|
resourceId: assetId,
|
|
608
829
|
accessLevelRemoteId: roleId,
|
|
609
830
|
});
|
|
610
831
|
break;
|
|
611
832
|
}
|
|
612
|
-
case
|
|
833
|
+
case graphql_2.EntityType.Group: {
|
|
613
834
|
requestedGroups.push({
|
|
614
835
|
groupId: assetId,
|
|
615
836
|
accessLevelRemoteId: roleId,
|
|
@@ -746,7 +967,7 @@ async function setCustomDuration(metadata) {
|
|
|
746
967
|
var _a, _b, _c;
|
|
747
968
|
const { d, h, m } = getDurationNumbers(answer);
|
|
748
969
|
const durationInMinutes = getDurationInMinutes(answer);
|
|
749
|
-
if (d < 0 || h < 0 || m < 0 || d + h + m
|
|
970
|
+
if (d < 0 || h < 0 || m < 0 || d + h + m === 0 || (h > 23 && m > 59)) {
|
|
750
971
|
return "Please enter a valid duration.";
|
|
751
972
|
}
|
|
752
973
|
if (((_a = metadata.requestDefaults) === null || _a === void 0 ? void 0 : _a.maxDurationInMinutes) &&
|
|
@@ -769,8 +990,8 @@ async function setCustomDuration(metadata) {
|
|
|
769
990
|
label: durationLabel,
|
|
770
991
|
};
|
|
771
992
|
}
|
|
772
|
-
async function
|
|
773
|
-
|
|
993
|
+
async function promptRequestSubmission(cmd, metadata) {
|
|
994
|
+
(0, displays_1.displayFinalRequestSummary)(cmd, metadata);
|
|
774
995
|
const submitMessage = "ā
Yes, submit request";
|
|
775
996
|
const cancelMessage = "ā No, cancel request";
|
|
776
997
|
const { submit } = await inquirer.prompt([
|
|
@@ -783,55 +1004,6 @@ async function submitFinalRequest(cmd, client, metadata) {
|
|
|
783
1004
|
]);
|
|
784
1005
|
switch (submit) {
|
|
785
1006
|
case submitMessage: {
|
|
786
|
-
// Build requested assets lists for the mutation
|
|
787
|
-
const requestedResources = [];
|
|
788
|
-
const requestedGroups = [];
|
|
789
|
-
for (const appNode of Object.values(metadata.requestMap)) {
|
|
790
|
-
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
791
|
-
if (assetNode.roles !== undefined) {
|
|
792
|
-
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
793
|
-
return roleId;
|
|
794
|
-
});
|
|
795
|
-
const roleIds = mappedRoles.length ? mappedRoles : [""];
|
|
796
|
-
for (const roleId of roleIds) {
|
|
797
|
-
switch (assetNode.type) {
|
|
798
|
-
case "Resource": {
|
|
799
|
-
requestedResources.push({
|
|
800
|
-
resourceId: assetId,
|
|
801
|
-
accessLevel: {
|
|
802
|
-
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
803
|
-
accessLevelRemoteId: roleId,
|
|
804
|
-
},
|
|
805
|
-
});
|
|
806
|
-
break;
|
|
807
|
-
}
|
|
808
|
-
case "Group": {
|
|
809
|
-
requestedGroups.push({
|
|
810
|
-
groupId: assetId,
|
|
811
|
-
accessLevel: {
|
|
812
|
-
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
813
|
-
accessLevelRemoteId: roleId,
|
|
814
|
-
},
|
|
815
|
-
});
|
|
816
|
-
break;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
824
|
-
// Build link to request
|
|
825
|
-
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
826
|
-
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
827
|
-
cmd.log("\nš Your Access Request has been submitted!\n");
|
|
828
|
-
cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
|
|
829
|
-
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
830
|
-
cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
|
|
831
|
-
}
|
|
832
|
-
const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
|
|
833
|
-
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
834
|
-
}
|
|
835
1007
|
return;
|
|
836
1008
|
}
|
|
837
1009
|
case cancelMessage: {
|
|
@@ -843,3 +1015,142 @@ async function submitFinalRequest(cmd, client, metadata) {
|
|
|
843
1015
|
}
|
|
844
1016
|
}
|
|
845
1017
|
}
|
|
1018
|
+
async function submitFinalRequest(cmd, client, metadata) {
|
|
1019
|
+
var _a, _b, _c, _d;
|
|
1020
|
+
// Build requested assets lists for the mutation
|
|
1021
|
+
const requestedResources = [];
|
|
1022
|
+
const requestedGroups = [];
|
|
1023
|
+
for (const appNode of Object.values(metadata.requestMap)) {
|
|
1024
|
+
// This extraction is different than the one in setRequestDefaults.
|
|
1025
|
+
// Both extract the requestedResources and requestedGroups,
|
|
1026
|
+
// use different formats.
|
|
1027
|
+
for (const [assetId, assetNode] of Object.entries(appNode.assets)) {
|
|
1028
|
+
if (assetNode.roles) {
|
|
1029
|
+
const mappedRoles = Object.entries(assetNode.roles).map(([roleId, _roleNode]) => {
|
|
1030
|
+
return roleId;
|
|
1031
|
+
});
|
|
1032
|
+
const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
|
|
1033
|
+
for (const roleId of roleIds) {
|
|
1034
|
+
switch (assetNode.type) {
|
|
1035
|
+
case graphql_2.EntityType.Resource: {
|
|
1036
|
+
requestedResources.push({
|
|
1037
|
+
resourceId: assetId,
|
|
1038
|
+
accessLevel: {
|
|
1039
|
+
accessLevelName: ((_b = (_a = assetNode.roles) === null || _a === void 0 ? void 0 : _a[roleId]) === null || _b === void 0 ? void 0 : _b.roleName) || "",
|
|
1040
|
+
accessLevelRemoteId: roleId,
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
case graphql_2.EntityType.Group: {
|
|
1046
|
+
requestedGroups.push({
|
|
1047
|
+
groupId: assetId,
|
|
1048
|
+
accessLevel: {
|
|
1049
|
+
accessLevelName: ((_d = (_c = assetNode.roles) === null || _c === void 0 ? void 0 : _c[roleId]) === null || _d === void 0 ? void 0 : _d.roleName) || "",
|
|
1050
|
+
accessLevelRemoteId: roleId,
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const resp = await createRequest(cmd, client, requestedResources, requestedGroups, metadata.reason, metadata.durationInMinutes);
|
|
1061
|
+
// Build link to request
|
|
1062
|
+
const configData = (0, config_1.getOrCreateConfigData)(cmd.config.configDir);
|
|
1063
|
+
if (resp === null || resp === void 0 ? void 0 : resp.id) {
|
|
1064
|
+
cmd.log("\nš Your Access Request has been submitted!\n");
|
|
1065
|
+
cmd.log(`${chalk_1.default.bold("ID: ")} ${chalk_1.default.cyan(resp === null || resp === void 0 ? void 0 : resp.id)}`);
|
|
1066
|
+
if (resp === null || resp === void 0 ? void 0 : resp.status) {
|
|
1067
|
+
cmd.log((0, displays_1.getStyledStatus)(resp === null || resp === void 0 ? void 0 : resp.status));
|
|
1068
|
+
}
|
|
1069
|
+
const requestLink = `${configData[config_1.urlKey]}/requests/sent/${resp === null || resp === void 0 ? void 0 : resp.id}`;
|
|
1070
|
+
cmd.log(`${chalk_1.default.bold("Link:")} ${chalk_1.default.underline(requestLink)}\n`);
|
|
1071
|
+
}
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
async function bypassRequestSelection(cmd, client, flagValue, metadata) {
|
|
1075
|
+
var _a, _b;
|
|
1076
|
+
try {
|
|
1077
|
+
// Query Catalog Item endpoint to identify what the id belongs to (resource or group)
|
|
1078
|
+
for (const id of flagValue) {
|
|
1079
|
+
const resp = await client.query({
|
|
1080
|
+
query: CATALOG_ITEM,
|
|
1081
|
+
variables: {
|
|
1082
|
+
uuid: id || "",
|
|
1083
|
+
},
|
|
1084
|
+
fetchPolicy: "network-only", // to avoid caching
|
|
1085
|
+
});
|
|
1086
|
+
switch (resp.data.catalogItem.__typename) {
|
|
1087
|
+
case "Resource": {
|
|
1088
|
+
const resourceName = resp.data.catalogItem.displayName;
|
|
1089
|
+
const roles = (resp.data.catalogItem.accessLevels || [])
|
|
1090
|
+
// TODO: Support okta azure apps ?.filter((role) => role.accessLevelName !== "") // This assumes length == 1
|
|
1091
|
+
.map((role) => ({
|
|
1092
|
+
id: role.accessLevelRemoteId,
|
|
1093
|
+
name: role.accessLevelName,
|
|
1094
|
+
}));
|
|
1095
|
+
if (roles.length > 0 &&
|
|
1096
|
+
!(roles.length === 1 && roles[0].name === "")) {
|
|
1097
|
+
cmd.log(`Roles not implemented yet for resource ${resourceName}. Skipping roles selection.`); //TODO: Implement roles support
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
const appId = ((_a = resp.data.catalogItem.connection) === null || _a === void 0 ? void 0 : _a.id) || "";
|
|
1101
|
+
if (!(appId in metadata.requestMap)) {
|
|
1102
|
+
metadata.requestMap[appId] = {
|
|
1103
|
+
appName: ((_b = resp.data.catalogItem.connection) === null || _b === void 0 ? void 0 : _b.displayName) || "",
|
|
1104
|
+
appId: appId,
|
|
1105
|
+
assets: {},
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
const assetEntry = metadata.requestMap[appId].assets[id];
|
|
1109
|
+
if (!assetEntry) {
|
|
1110
|
+
metadata.requestMap[appId].assets[id] = {
|
|
1111
|
+
assetId: id,
|
|
1112
|
+
assetName: resourceName,
|
|
1113
|
+
type: graphql_2.EntityType.Resource,
|
|
1114
|
+
roles: {},
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
if (roles && !(roles.length === 1 && roles[0].name === "")) {
|
|
1118
|
+
if (!metadata.requestMap[appId].assets[id].roles) {
|
|
1119
|
+
metadata.requestMap[appId].assets[id].roles = {};
|
|
1120
|
+
}
|
|
1121
|
+
for (const role of roles) {
|
|
1122
|
+
metadata.requestMap[appId].assets[id].roles[role.id] = {
|
|
1123
|
+
roleId: role.id,
|
|
1124
|
+
roleName: role.name,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
case "Group": {
|
|
1131
|
+
//TODO
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
case "Connection": {
|
|
1135
|
+
//TODO
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
default:
|
|
1139
|
+
cmd.error("Unknown error occurred.");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
if (error instanceof Error || typeof error === "string") {
|
|
1145
|
+
cmd.error(error);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
function bypassDuration(cmd, duration, metadata) {
|
|
1151
|
+
const maxDuration = metadata.requestDefaults.maxDurationInMinutes;
|
|
1152
|
+
if (maxDuration && duration > maxDuration) {
|
|
1153
|
+
cmd.error(`The requested duration exceeds the allowed limit of ${maxDuration}`);
|
|
1154
|
+
}
|
|
1155
|
+
metadata.durationInMinutes = duration;
|
|
1156
|
+
}
|
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 {
|