opal-security 5.0.1 → 5.1.1

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.
@@ -0,0 +1,3 @@
1
+ import type { Hook } from "@oclif/core";
2
+ declare const hook: Hook<"init">;
3
+ export default hook;
@@ -0,0 +1,110 @@
1
+ import http from "node:http";
2
+ import https from "node:https";
3
+ import chalk from "chalk";
4
+ import { allowSelfSignedCertsKey, getOrCreateConfigData, urlKey, } from "../../lib/config.js";
5
+ // Commands that should skip the version check (e.g., configuration commands)
6
+ const SKIP_VERSION_CHECK_COMMANDS = [
7
+ "set-url",
8
+ "set-token",
9
+ "set-auth-config",
10
+ "version",
11
+ "help",
12
+ "autocomplete",
13
+ ];
14
+ const hook = async (opts) => {
15
+ // Skip version check for certain commands
16
+ if (opts.id && SKIP_VERSION_CHECK_COMMANDS.includes(opts.id)) {
17
+ return;
18
+ }
19
+ try {
20
+ const configData = getOrCreateConfigData(opts.config.configDir);
21
+ const baseUrl = configData[urlKey];
22
+ const allowSelfSignedCerts = configData[allowSelfSignedCertsKey];
23
+ if (!baseUrl) {
24
+ return;
25
+ }
26
+ const cliVersion = opts.config.version;
27
+ const url = new URL("/api/cli/version-check", baseUrl);
28
+ url.searchParams.set("version", cliVersion);
29
+ const response = await fetchWithTimeout(url.toString(), {
30
+ method: "GET",
31
+ headers: {
32
+ "User-Agent": `Opal CLI v${cliVersion}`,
33
+ },
34
+ }, allowSelfSignedCerts, 5000);
35
+ if (!response.ok) {
36
+ // Silently fail on non-2xx responses - don't block the CLI
37
+ return;
38
+ }
39
+ const data = JSON.parse(response.body);
40
+ if (data.status === "ok") {
41
+ return;
42
+ }
43
+ // Display message based on status
44
+ const formattedMessage = formatMessage(data.status, data.message);
45
+ if (formattedMessage) {
46
+ process.stderr.write(`${formattedMessage}\n`);
47
+ }
48
+ }
49
+ catch (_a) {
50
+ // Silently fail on any errors (network issues, etc.) - don't block the CLI
51
+ }
52
+ };
53
+ function formatMessage(status, message) {
54
+ if (!message) {
55
+ return "";
56
+ }
57
+ switch (status) {
58
+ case "error":
59
+ return chalk.red(`❗ ${message}`);
60
+ case "warn":
61
+ return chalk.yellow(`⚠️ ${message}`);
62
+ case "info":
63
+ return chalk.blue(`ℹ️ ${message}`);
64
+ default:
65
+ return message;
66
+ }
67
+ }
68
+ function fetchWithTimeout(url, options, allowSelfSignedCerts, timeoutMs) {
69
+ return new Promise((resolve, reject) => {
70
+ const parsedUrl = new URL(url);
71
+ const isHttps = parsedUrl.protocol === "https:";
72
+ const lib = isHttps ? https : http;
73
+ const requestOptions = {
74
+ hostname: parsedUrl.hostname,
75
+ port: parsedUrl.port || (isHttps ? 443 : 80),
76
+ path: parsedUrl.pathname + parsedUrl.search,
77
+ method: options.method,
78
+ headers: options.headers,
79
+ timeout: timeoutMs,
80
+ };
81
+ if (isHttps) {
82
+ requestOptions.rejectUnauthorized =
83
+ !allowSelfSignedCerts;
84
+ }
85
+ const req = lib.request(requestOptions, (res) => {
86
+ let body = "";
87
+ res.on("data", (chunk) => {
88
+ body += chunk;
89
+ });
90
+ res.on("end", () => {
91
+ resolve({
92
+ ok: res.statusCode !== undefined &&
93
+ res.statusCode >= 200 &&
94
+ res.statusCode !== undefined &&
95
+ res.statusCode >= 200 &&
96
+ res.statusCode < 300,
97
+ status: res.statusCode || 0,
98
+ body,
99
+ });
100
+ });
101
+ });
102
+ req.on("error", reject);
103
+ req.on("timeout", () => {
104
+ req.destroy();
105
+ reject(new Error("Request timed out"));
106
+ });
107
+ req.end();
108
+ });
109
+ }
110
+ export default hook;
package/build/labels.d.ts CHANGED
@@ -1,3 +1,7 @@
1
- import { ConnectionType, EntityType } from "./graphql/graphql.js";
1
+ import { ConnectionType, EntityType, GroupType, ResourceType } from "./graphql/graphql.js";
2
2
  export declare const connectionTypeLabelByType: Record<ConnectionType, string>;
3
- export declare const DisplayLabels: Partial<Record<EntityType, string>>;
3
+ export declare const EntityTypeLabels: Partial<Record<EntityType, string>>;
4
+ export declare const ResourceTypeLabels: Record<ResourceType, string>;
5
+ export declare const GroupTypeLabels: Record<GroupType, string>;
6
+ export declare function getAssetTypeLabel(entityType: EntityType, assetType: ResourceType | GroupType | undefined): string;
7
+ export declare function getAppTypeLabel(appType: ConnectionType | ResourceType): string;
package/build/labels.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ConnectionType, EntityType } from "./graphql/graphql.js";
1
+ import { ConnectionType, EntityType, GroupType, ResourceType, } from "./graphql/graphql.js";
2
2
  export const connectionTypeLabelByType = {
3
3
  [ConnectionType.ActiveDirectory]: "Active Directory",
4
4
  [ConnectionType.Aws]: "Amazon Web Services (Legacy)",
@@ -34,8 +34,139 @@ export const connectionTypeLabelByType = {
34
34
  [ConnectionType.Cursor]: "Cursor",
35
35
  [ConnectionType.OpenaiPlatform]: "OpenAI Platform",
36
36
  [ConnectionType.OracleFusion]: "Oracle Fusion",
37
+ [ConnectionType.Devin]: "Devin",
38
+ [ConnectionType.Incidentio]: "incident.io",
39
+ [ConnectionType.OktaCiam]: "Okta CIAM",
40
+ [ConnectionType.Vault]: "HashiCorp Vault",
37
41
  };
38
- export const DisplayLabels = {
42
+ export const EntityTypeLabels = {
39
43
  [EntityType.Resource]: "Resource",
40
44
  [EntityType.Group]: "Group",
41
45
  };
46
+ // ResourceTypeLabels maps readable labels from model resource types
47
+ export const ResourceTypeLabels = {
48
+ [ResourceType.AwsEc2Instance]: "AWS EC2",
49
+ [ResourceType.AwsEksCluster]: "AWS EKS",
50
+ [ResourceType.AwsIamRole]: "AWS IAM Role",
51
+ [ResourceType.AwsAccount]: "AWS Account",
52
+ [ResourceType.AwsOrganizationalUnit]: "AWS Organizational Unit",
53
+ [ResourceType.AwsSsoPermissionSet]: "AWS Identity Center Role",
54
+ [ResourceType.AwsRdsMysqlInstance]: "AWS RDS MySQL Instance",
55
+ [ResourceType.AwsRdsMysqlCluster]: "AWS RDS MySQL Cluster",
56
+ [ResourceType.AwsRdsPostgresInstance]: "AWS RDS Postgres Instance",
57
+ [ResourceType.AwsRdsPostgresCluster]: "AWS RDS Postgres Cluster",
58
+ [ResourceType.Custom]: "Custom",
59
+ [ResourceType.CustomConnector]: "Connector Resource",
60
+ [ResourceType.GcpOrganization]: "GCP Organization",
61
+ [ResourceType.GcpBucket]: "GCP Bucket",
62
+ [ResourceType.GcpFolder]: "GCP Folder",
63
+ [ResourceType.GcpComputeInstance]: "GCP Compute",
64
+ [ResourceType.GcpCloudSqlMysqlInstance]: "GCP Cloud SQL MySQL",
65
+ [ResourceType.GcpCloudSqlPostgresInstance]: "GCP Cloud SQL Postgres",
66
+ [ResourceType.GcpGkeCluster]: "GCP GKE",
67
+ [ResourceType.GcpProject]: "GCP Project",
68
+ [ResourceType.GcpBigQueryDataset]: "GCP BigQuery Dataset",
69
+ [ResourceType.GcpBigQueryTable]: "GCP BigQuery Table",
70
+ [ResourceType.GcpServiceAccount]: "GCP Service Account",
71
+ [ResourceType.GoogleWorkspaceRole]: "Google Workspace Role",
72
+ [ResourceType.GitHubRepo]: "GitHub Repo",
73
+ [ResourceType.GitHubOrg]: "GitHub Organization",
74
+ [ResourceType.GitHubOrgRole]: "GitHub Organization Role",
75
+ [ResourceType.GitLabProject]: "GitLab Project",
76
+ [ResourceType.MongoInstance]: "Mongo",
77
+ [ResourceType.MongoAtlasInstance]: "Mongo Atlas",
78
+ [ResourceType.OktaApp]: "Okta App",
79
+ [ResourceType.OktaRole]: "Okta Role",
80
+ [ResourceType.OpalRole]: "Opal Role",
81
+ [ResourceType.OpalScopedRole]: "Opal Custom Role",
82
+ [ResourceType.PagerdutyRole]: "PagerDuty Role",
83
+ [ResourceType.TailscaleSsh]: "Tailscale SSH",
84
+ [ResourceType.SalesforcePermissionSet]: "SFDC Permission Set",
85
+ [ResourceType.SalesforceProfile]: "SFDC Profile",
86
+ [ResourceType.SalesforceRole]: "SFDC Role",
87
+ [ResourceType.SnowflakeDatabase]: "Snowflake Database",
88
+ [ResourceType.SnowflakeSchema]: "Snowflake Schema",
89
+ [ResourceType.SnowflakeTable]: "Snowflake Table",
90
+ [ResourceType.WorkdayRole]: "Workday Organization Role",
91
+ [ResourceType.MysqlInstance]: "MySQL",
92
+ [ResourceType.MariadbInstance]: "MariaDB",
93
+ [ResourceType.PostgresInstance]: "Postgres",
94
+ [ResourceType.TeleportRole]: "Teleport Role",
95
+ [ResourceType.AzureManagementGroup]: "Azure Management Group",
96
+ [ResourceType.AzureResourceGroup]: "Azure Resource Group",
97
+ [ResourceType.AzureSubscription]: "Azure Subscription",
98
+ [ResourceType.AzureVirtualMachine]: "Azure Virtual Machine",
99
+ [ResourceType.AzureStorageAccount]: "Azure Storage Account",
100
+ [ResourceType.AzureStorageContainer]: "Azure Storage Container",
101
+ [ResourceType.AzureSqlServer]: "Azure SQL Server",
102
+ [ResourceType.AzureSqlManagedInstance]: "Azure SQL Managed Instance",
103
+ [ResourceType.AzureSqlDatabase]: "Azure SQL Database",
104
+ [ResourceType.AzureSqlManagedDatabase]: "Azure SQL Managed Database",
105
+ [ResourceType.AzureUserAssignedManagedIdentity]: "Azure User-Assigned Managed Identity",
106
+ [ResourceType.AzureEntraIdRole]: "Microsoft Entra ID Role",
107
+ [ResourceType.AzureEnterpriseApp]: "Azure Enterprise Application",
108
+ [ResourceType.DatabricksAccountServicePrincipal]: "Databricks Account-Level Service Principal",
109
+ [ResourceType.IlevelAdvancedRole]: "iLEVEL Advanced Role",
110
+ [ResourceType.DatastaxAstraRole]: "Astra Role",
111
+ [ResourceType.CoupaRole]: "Coupa Role",
112
+ [ResourceType.CursorOrganization]: "Cursor Organization",
113
+ [ResourceType.OpenaiPlatformProject]: "OpenAI Platform Project",
114
+ [ResourceType.OpenaiPlatformServiceAccount]: "OpenAI Platform Service Account",
115
+ [ResourceType.AnthropicWorkspace]: "Anthropic Workspace",
116
+ [ResourceType.OracleFusionRole]: "Oracle Fusion Role",
117
+ [ResourceType.DevinOrganization]: "Devin Organization",
118
+ [ResourceType.DevinRole]: "Devin Role",
119
+ [ResourceType.VaultOidcRole]: "Vault OIDC Role",
120
+ [ResourceType.VaultPolicy]: "Vault Policy",
121
+ [ResourceType.VaultSecret]: "Vault Secret",
122
+ };
123
+ // GroupTypeLabels maps readable labels from model group types
124
+ export const GroupTypeLabels = {
125
+ [GroupType.ActiveDirectoryGroup]: "AD Group",
126
+ [GroupType.AwsSsoGroup]: "AWS IAM Group",
127
+ [GroupType.DuoGroup]: "Duo Group",
128
+ [GroupType.GitHubTeam]: "GitHub Team",
129
+ [GroupType.GitLabGroup]: "GitLab Group",
130
+ [GroupType.GoogleGroupsGroup]: "Google Group",
131
+ [GroupType.GoogleGroupsGkeGroup]: "GKE Google Group",
132
+ [GroupType.LdapGroup]: "LDAP Group",
133
+ [GroupType.OktaGroup]: "Okta Group",
134
+ [GroupType.OpalGroup]: "Opal Group",
135
+ [GroupType.AzureAdMicrosoft_365Group]: "Microsoft 365 Group",
136
+ [GroupType.AzureAdSecurityGroup]: "Microsoft Entra ID Security Group",
137
+ [GroupType.ConnectorGroup]: "Connector Group",
138
+ [GroupType.SnowflakeRole]: "Snowflake Role",
139
+ [GroupType.TailscaleGroup]: "Tailscale Group",
140
+ [GroupType.WorkdayUserSecurityGroup]: "Workday User Security Group",
141
+ [GroupType.OpalAccessRule]: "Access Rule",
142
+ [GroupType.OktaGroupRule]: "Okta Group Rule",
143
+ [GroupType.DatabricksAccountGroup]: "Databricks Account-Level Group",
144
+ [GroupType.DevinGroup]: "Devin Group",
145
+ [GroupType.IncidentioOnCallSchedule]: "incident.io On-Call Schedule",
146
+ [GroupType.PagerdutyOnCallSchedule]: "PagerDuty On-Call Schedule",
147
+ };
148
+ export function getAssetTypeLabel(entityType, assetType) {
149
+ var _a;
150
+ if (assetType === undefined) {
151
+ // If asset type is not defined (e.g., Okta/Azure group), return empty string
152
+ return "";
153
+ }
154
+ let assetTypeLabel = (_a = EntityTypeLabels[entityType]) !== null && _a !== void 0 ? _a : entityType;
155
+ if (entityType === EntityType.Resource) {
156
+ assetTypeLabel =
157
+ ResourceTypeLabels[assetType] || assetTypeLabel;
158
+ }
159
+ else if (entityType === EntityType.Group) {
160
+ assetTypeLabel = GroupTypeLabels[assetType] || assetTypeLabel;
161
+ }
162
+ return `[${assetTypeLabel}]`;
163
+ }
164
+ export function getAppTypeLabel(appType) {
165
+ if (appType in connectionTypeLabelByType) {
166
+ return connectionTypeLabelByType[appType];
167
+ }
168
+ if (appType in ResourceTypeLabels) {
169
+ return ResourceTypeLabels[appType];
170
+ }
171
+ return "App";
172
+ }
@@ -1,4 +1,5 @@
1
1
  import { graphql } from "../../../../graphql/index.js";
2
+ import { ResourceTypeLabels, connectionTypeLabelByType, } from "../../../../labels.js";
2
3
  // TODO: add pagination ability from CLI. (Load more...) option
3
4
  const GET_REQUESTABLE_APPS_QUERY = graphql(`
4
5
  query GetRequestableAppsQuery($searchQuery: String) {
@@ -41,18 +42,21 @@ export async function queryRequestableApps(cmd, client, input) {
41
42
  });
42
43
  return (_b = (_a = resp === null || resp === void 0 ? void 0 : resp.data) === null || _a === void 0 ? void 0 : _a.appsV2) === null || _b === void 0 ? void 0 : _b.edges.map((edge) => {
43
44
  let type = undefined;
45
+ let message = "";
44
46
  switch (edge.node.__typename) {
45
47
  case "Resource":
46
48
  type = edge.node.resourceType;
49
+ message = `${edge.node.displayName} [${ResourceTypeLabels[type]}]`;
47
50
  break;
48
51
  case "Connection":
49
52
  type = edge.node.connectionType;
53
+ message = `${edge.node.displayName} [${connectionTypeLabelByType[type]}]`;
50
54
  break;
51
55
  default:
52
56
  type = edge.node.__typename;
53
57
  }
54
58
  return {
55
- message: `${edge.node.displayName} [${type}]`,
59
+ message: message,
56
60
  value: {
57
61
  id: edge.node.id,
58
62
  name: edge.node.displayName,
@@ -1,4 +1,5 @@
1
1
  import { graphql } from "../../../../graphql/index.js";
2
+ import { getAssetTypeLabel } from "../../../../labels.js";
2
3
  import { entityTypeFromString } from "../../types.js";
3
4
  const GET_ASSETS_QUERY = graphql(`
4
5
  query PaginatedEntityDropdown(
@@ -21,10 +22,12 @@ const GET_ASSETS_QUERY = graphql(`
21
22
  resource {
22
23
  id
23
24
  name
25
+ resourceType
24
26
  }
25
27
  group {
26
28
  id
27
29
  name
30
+ groupType
28
31
  }
29
32
  }
30
33
  cursor
@@ -55,13 +58,17 @@ export async function queryRequestableAssets(cmd, client, appId, input) {
55
58
  var _a, _b, _c, _d, _e, _f, _g, _h;
56
59
  const name = ((_a = item.resource) === null || _a === void 0 ? void 0 : _a.name) || ((_b = item.group) === null || _b === void 0 ? void 0 : _b.name);
57
60
  const id = ((_c = item.resource) === null || _c === void 0 ? void 0 : _c.id) || ((_d = item.group) === null || _d === void 0 ? void 0 : _d.id);
58
- const type = ((_e = item.resource) === null || _e === void 0 ? void 0 : _e.__typename) || ((_f = item.group) === null || _f === void 0 ? void 0 : _f.__typename);
61
+ const entityType = entityTypeFromString(((_e = item.resource) === null || _e === void 0 ? void 0 : _e.__typename) || ((_f = item.group) === null || _f === void 0 ? void 0 : _f.__typename));
62
+ const assetType = ((_g = item.resource) === null || _g === void 0 ? void 0 : _g.resourceType) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.groupType);
63
+ const assetTypeLabel = getAssetTypeLabel(entityType, assetType);
59
64
  return {
60
- message: `${name} [${type}]`,
65
+ message: `${name} ${assetTypeLabel}`,
61
66
  value: {
62
67
  name: name || "",
63
68
  id: id || "",
64
- type: entityTypeFromString(((_g = item.resource) === null || _g === void 0 ? void 0 : _g.__typename) || ((_h = item.group) === null || _h === void 0 ? void 0 : _h.__typename)),
69
+ entityType: entityType,
70
+ assetType: assetType,
71
+ toString: () => name || "",
65
72
  },
66
73
  };
67
74
  });
@@ -1,6 +1,6 @@
1
1
  import { graphql } from "../../../../graphql/index.js";
2
2
  import { EntityType, } from "../../../../graphql/graphql.js";
3
- import { DisplayLabels } from "../../../../labels.js";
3
+ import { EntityTypeLabels, GroupTypeLabels } from "../../../../labels.js";
4
4
  const RESOURCE_ROLES_QUERY = graphql(`
5
5
  query ResourceAccessLevels($resourceId: ResourceId!) {
6
6
  accessLevels(input: {
@@ -61,6 +61,7 @@ export async function queryAssetRoles(cmd, client, assetType, assetId) {
61
61
  value: {
62
62
  name: role.accessLevelName || "",
63
63
  id: role.accessLevelRemoteId || "",
64
+ entityType: EntityType.Resource,
64
65
  },
65
66
  };
66
67
  });
@@ -90,6 +91,7 @@ export async function queryAssetRoles(cmd, client, assetType, assetId) {
90
91
  value: {
91
92
  name: role.accessLevelName,
92
93
  id: role.accessLevelRemoteId,
94
+ entityType: EntityType.Group,
93
95
  },
94
96
  };
95
97
  });
@@ -136,7 +138,11 @@ const ASSOCIATED_ITEMS_QUERY = graphql(`
136
138
  __typename
137
139
  id
138
140
  name
141
+ ... on Group {
142
+ groupType
143
+ }
139
144
  ... on Resource {
145
+ resourceType
140
146
  accessLevels(
141
147
  filters: {
142
148
  skipRemoteAccessLevels: false # azure app roles are remote
@@ -169,7 +175,6 @@ function appRolesFromEdge(edge) {
169
175
  value: {
170
176
  id: edge.node.id + accessLevel.accessLevelRemoteId,
171
177
  name: accessLevel.accessLevelName,
172
- type: DisplayLabels[EntityType.Resource],
173
178
  toString: () => accessLevel.accessLevelName,
174
179
  },
175
180
  }));
@@ -180,7 +185,7 @@ function appRolesFromEdge(edge) {
180
185
  value: {
181
186
  id: edge.node.id,
182
187
  name: (_b = edge.alias) !== null && _b !== void 0 ? _b : edge.node.name,
183
- type: DisplayLabels[EntityType.Resource],
188
+ type: EntityTypeLabels[EntityType.Resource],
184
189
  toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
185
190
  },
186
191
  },
@@ -189,11 +194,12 @@ function appRolesFromEdge(edge) {
189
194
  case "Group":
190
195
  return [
191
196
  {
192
- message: `${(_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name} ${EntityType.Group}`,
197
+ message: `${(_c = edge.alias) !== null && _c !== void 0 ? _c : edge.node.name} [${GroupTypeLabels[edge.node.groupType]}]`,
193
198
  value: {
194
199
  id: edge.node.id,
195
200
  name: (_d = edge.alias) !== null && _d !== void 0 ? _d : edge.node.name,
196
- type: DisplayLabels[EntityType.Group],
201
+ type: EntityTypeLabels[EntityType.Group],
202
+ assetType: edge.node.groupType,
197
203
  toString: () => { var _a; return (_a = edge.alias) !== null && _a !== void 0 ? _a : edge.node.name; },
198
204
  },
199
205
  },
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import Table from "cli-table3";
3
- import { DisplayLabels } from "../../labels.js";
3
+ import { getAppTypeLabel, getAssetTypeLabel } from "../../labels.js";
4
4
  export function headerMessage(cmd) {
5
5
  console.clear();
6
6
  cmd.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
@@ -10,21 +10,26 @@ export function headerMessage(cmd) {
10
10
  /*
11
11
  Example treeified output:
12
12
 
13
- Opal [App]
14
- └── Infosec Group [Group]
15
- └── Infosec Prod Databases [Group]
16
- MongoDB Atlas Test [App]
17
- └── my-mongo-db-atlas [Resource]
13
+ Opal [AppType]
14
+ └── Infosec Group [GroupType]
15
+ └── Infosec Prod Databases [GroupType]
16
+ MongoDB Atlas Test [AppType]
17
+ └── my-mongo-db-atlas [ResourceType]
18
18
  └── admin:test [Role]
19
19
  */
20
20
  export function treeifyRequestMap(cmd, requestMap) {
21
+ headerMessage(cmd);
21
22
  for (const [_appId, appNode] of Object.entries(requestMap)) {
22
23
  // Print App title first (without tree lines)
23
- cmd.log(`${chalk.bold(appNode.appName)} ${chalk.dim("[App]")}`);
24
+ const appTypeLabel = appNode.appType
25
+ ? getAppTypeLabel(appNode.appType)
26
+ : "App";
27
+ cmd.log(`${chalk.bold(appNode.appName)} ${chalk.dim(`[${appTypeLabel}]`)}`);
24
28
  for (const [_assetId, assetNode] of Object.entries(appNode.assets)) {
25
29
  // If okta/azure asset with no role, change asset name
26
30
  const assetName = assetNode.assetName || "No Role (Direct access)";
27
- cmd.log(`└── ${assetName} ${chalk.dim(`[${DisplayLabels[assetNode.type]}]`)}`);
31
+ const assetTypeLabel = getAssetTypeLabel(assetNode.entityType, assetNode.assetType);
32
+ cmd.log(`└── ${assetName} ${chalk.dim(`${assetTypeLabel}`)}`);
28
33
  if (assetNode.roles !== undefined) {
29
34
  for (const [_roleId, roleNode] of Object.entries(assetNode.roles)) {
30
35
  const roleName = roleNode.roleName;
@@ -5,6 +5,7 @@ import enquirer from "enquirer-esm";
5
5
  // @ts-expect-error - enquirer-esm doesn't export autocomplete in types but it exists at runtime
6
6
  const { autocomplete } = enquirer;
7
7
  export async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
8
+ var _a;
8
9
  const associatedItems = (await queryAssociatedItems(cmd, client, app.id, "")) || [];
9
10
  const selectedRole = await autocomplete({
10
11
  name: "Roles",
@@ -24,7 +25,8 @@ export async function chooseOktaAzureRoles(cmd, client, app, requestMap) {
24
25
  entry.assets[selectedRole.id] = {
25
26
  assetId: selectedRole.id,
26
27
  assetName: selectedRole.name,
27
- type: entityTypeFromString(selectedRole.type),
28
+ entityType: entityTypeFromString(selectedRole.type),
29
+ assetType: (_a = selectedRole.assetType) !== null && _a !== void 0 ? _a : undefined,
28
30
  roles: {},
29
31
  };
30
32
  }
@@ -53,7 +55,8 @@ export async function chooseAssets(cmd, client, appId, requestMap) {
53
55
  entry.assets[selectedAsset.id] = {
54
56
  assetId: selectedAsset.id,
55
57
  assetName: selectedAsset.name,
56
- type: selectedAsset.type,
58
+ entityType: selectedAsset.entityType,
59
+ assetType: selectedAsset.assetType,
57
60
  roles: {},
58
61
  };
59
62
  }
@@ -10,7 +10,7 @@ export async function chooseRoles(cmd, client, appId, assetId, requestMap) {
10
10
  if (entry === undefined || assetEntry === undefined) {
11
11
  throw new Error(`App ${appId} or Asset ${assetId} not found in requestMap`);
12
12
  }
13
- const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.type, assetId))) !== null && _a !== void 0 ? _a : [];
13
+ const assetRoles = (_a = (await queryAssetRoles(cmd, client, assetEntry.entityType, assetId))) !== null && _a !== void 0 ? _a : [];
14
14
  if (assetRoles !== undefined &&
15
15
  (assetRoles.length === 0 ||
16
16
  (assetRoles.length === 1 && assetRoles[0].value.name === ""))) {
@@ -1,6 +1,6 @@
1
1
  import type { ApolloClient } from "@apollo/client";
2
2
  import type { Command } from "@oclif/core";
3
- import { type ConnectionType, RequestMessageCode } from "../../graphql/graphql.js";
3
+ import { type ConnectionType, RequestMessageCode, type ResourceType } from "../../graphql/graphql.js";
4
4
  import { type RequestMap, type RequestMetadata } from "./types.js";
5
5
  export declare function initEmptyRequestMetadata(): RequestMetadata;
6
6
  export declare function setRequestDefaults(cmd: Command, client: ApolloClient, metadata: RequestMetadata): Promise<void>;
@@ -9,6 +9,6 @@ export declare function getRequestLink(cmd: Command, id: string): string;
9
9
  export declare function generateRequestLink(cmd: Command, defaultDurationInMinutes: number): string;
10
10
  export declare function bypassRequestSelection(cmd: Command, client: ApolloClient, flagValue: string[], metadata: RequestMetadata): Promise<void>;
11
11
  export declare function bypassDuration(cmd: Command, duration: number, metadata: RequestMetadata): void;
12
- export declare function getRequestMessageFromCode(cmd: Command, code: RequestMessageCode, connectionName: string | undefined, connectionType: ConnectionType | undefined, extraParams?: string, sourceGroupRedirect?: () => void): string;
12
+ export declare function getRequestMessageFromCode(cmd: Command, code: RequestMessageCode, appName: string | undefined, appType: ConnectionType | ResourceType | undefined, extraParams?: string, sourceGroupRedirect?: () => void): string;
13
13
  export declare function duplicateRequestTemplate(cmd: Command, client: ApolloClient, requestId: string, metadata: RequestMetadata): Promise<void>;
14
14
  export declare function copyBundleAssets(cmd: Command, client: ApolloClient, bundleId: string, requestMap: RequestMap): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { EntityType, RequestMessageCode, RequestStatus, ThirdPartyProvider, } from "../../graphql/graphql.js";
3
- import { connectionTypeLabelByType } from "../../labels.js";
3
+ import { getAppTypeLabel } from "../../labels.js";
4
4
  import { getOrCreateConfigData, urlKey } from "../../lib/config.js";
5
5
  import { createRequest, queryCatalogItems, queryRequest, queryRequestDefaults, } from "./api/index.js";
6
6
  import { queryBundle } from "./api/queries/requests.js";
@@ -49,7 +49,7 @@ export async function setRequestDefaults(cmd, client, metadata) {
49
49
  });
50
50
  const roleIds = mappedRoles.length ? mappedRoles : [""];
51
51
  for (const roleId of roleIds) {
52
- switch (assetNode.type) {
52
+ switch (assetNode.entityType) {
53
53
  case EntityType.Resource: {
54
54
  requestedResources.push({
55
55
  resourceId: assetId,
@@ -86,22 +86,22 @@ export async function setRequestDefaults(cmd, client, metadata) {
86
86
  metadata.requestDefaults.requesterIsAdmin =
87
87
  requestDefaults.requesterIsAdmin;
88
88
  for (const message of requestDefaults.messages || []) {
89
- let connectionName = undefined;
90
- let connectionType = undefined;
89
+ let appName = undefined;
90
+ let appType = undefined;
91
91
  // If the message has an entityId, retrieve the connection name and type
92
92
  if (message.entityId) {
93
93
  for (const appNode of Object.values(requestMap)) {
94
94
  for (const assetNode of Object.values(appNode.assets)) {
95
95
  if (assetNode.assetId === message.entityId) {
96
- connectionName = appNode.appName;
97
- connectionType = appNode.appType;
96
+ appName = appNode.appName;
97
+ appType = appNode.appType;
98
98
  break;
99
99
  }
100
100
  }
101
101
  }
102
102
  }
103
103
  // Log the request message based on the code
104
- cmd.log(chalk.dim(`\n${chalk.italic(message.level)}:`), getRequestMessageFromCode(cmd, message.code, connectionName, connectionType));
104
+ cmd.log(chalk.dim(`\n${chalk.italic(message.level)}:`), getRequestMessageFromCode(cmd, message.code, appName, appType));
105
105
  if (message.level === "ERROR") {
106
106
  process.exit(1);
107
107
  }
@@ -134,7 +134,7 @@ export async function submitFinalRequest(cmd, client, metadata) {
134
134
  });
135
135
  const roleIds = mappedRoles.length > 0 ? mappedRoles : [""];
136
136
  for (const roleId of roleIds) {
137
- switch (assetNode.type) {
137
+ switch (assetNode.entityType) {
138
138
  case EntityType.Resource: {
139
139
  requestedResources.push({
140
140
  resourceId: assetId,
@@ -216,7 +216,7 @@ export async function bypassRequestSelection(cmd, client, flagValue, metadata) {
216
216
  metadata.requestMap[appId].assets[assetId] = {
217
217
  assetId: assetId,
218
218
  assetName: assetName,
219
- type: entityTypeFromString(item.__typename),
219
+ entityType: entityTypeFromString(item.__typename),
220
220
  roles: {},
221
221
  };
222
222
  }
@@ -264,23 +264,23 @@ export function bypassDuration(cmd, duration, metadata) {
264
264
  metadata.durationInMinutes = duration;
265
265
  }
266
266
  }
267
- export function getRequestMessageFromCode(cmd, code, connectionName, connectionType, extraParams,
267
+ export function getRequestMessageFromCode(cmd, code, appName, appType, extraParams,
268
268
  // sourceGroup?: PropsFor<typeof GroupBindingGroupLabel>["group"];
269
269
  sourceGroupRedirect) {
270
270
  switch (code) {
271
271
  case RequestMessageCode.RequireUserAuthToken: {
272
- if (connectionType) {
273
- const connectionLabel = connectionTypeLabelByType[connectionType];
272
+ if (appType) {
273
+ const appLabel = getAppTypeLabel(appType);
274
274
  // This case the connection has a third party provider such as GitLab,
275
275
  // GitHub that requires the use to create a manual step into the end
276
276
  // system.
277
- const isThirdPartyProvider = Object.values(ThirdPartyProvider).find((v) => v === connectionType);
277
+ const isThirdPartyProvider = Object.values(ThirdPartyProvider).find((v) => v === appType);
278
278
  if (isThirdPartyProvider) {
279
279
  const configData = getOrCreateConfigData(cmd.config.configDir);
280
- return `This item requires you to link your ${connectionLabel} account to Opal before requesting access.
281
- You can link your ${connectionLabel} account at ${`${configData[urlKey]}/user/settings/identities`}`;
280
+ return `This item requires you to link your ${appLabel} account to Opal before requesting access.
281
+ You can link your ${appLabel} account at ${`${configData[urlKey]}/user/settings/identities`}`;
282
282
  }
283
- const name = connectionName !== null && connectionName !== void 0 ? connectionName : connectionLabel;
283
+ const name = appName !== null && appName !== void 0 ? appName : appLabel;
284
284
  // This case would be hit in case the user is not provisioned onto the end system, such as AWS, GCP, Okta, etc.
285
285
  return `This item requires you to be provisioned on ${name} before requesting access.\
286
286
  Please contact your Opal admin to provision your user on ${name}.`;
@@ -302,7 +302,7 @@ function addRequestedResourcesToMetadata(requestedResources, requestMap) {
302
302
  if (!resource)
303
303
  continue;
304
304
  const { id: assetId, displayName: assetName, connection, connectionId, __typename, } = resource;
305
- const type = entityTypeFromString(__typename);
305
+ const entityType = entityTypeFromString(__typename);
306
306
  const roleId = accessLevel.accessLevelRemoteId;
307
307
  const roleName = accessLevel.accessLevelName;
308
308
  if (!requestMap[connectionId]) {
@@ -318,7 +318,7 @@ function addRequestedResourcesToMetadata(requestedResources, requestMap) {
318
318
  appAssets[assetId] = {
319
319
  assetId,
320
320
  assetName,
321
- type,
321
+ entityType,
322
322
  roles: {},
323
323
  };
324
324
  }
@@ -334,7 +334,7 @@ function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
334
334
  if (!group)
335
335
  continue;
336
336
  const { id: assetId, name: assetName, connection, connectionId, __typename, } = group;
337
- const type = entityTypeFromString(__typename);
337
+ const entityType = entityTypeFromString(__typename);
338
338
  const roleId = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelRemoteId;
339
339
  const roleName = accessLevel === null || accessLevel === void 0 ? void 0 : accessLevel.accessLevelName;
340
340
  if (!requestMap[connectionId]) {
@@ -350,7 +350,7 @@ function addRequestedGroupsToMetadata(requestedGroups, requestMap) {
350
350
  appAssets[assetId] = {
351
351
  assetId,
352
352
  assetName,
353
- type,
353
+ entityType,
354
354
  roles: {},
355
355
  };
356
356
  }