Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { CommonVariables } from "./components/admin_tools/variables/CommonVariab
import { SecuredVariables } from "./components/admin_tools/variables/SecuredVariables.tsx";
import { Domains } from "./components/admin_tools/domains/Domains.tsx";
import { ActionsLog } from "./components/admin_tools/ActionsLog.tsx";
import { AccessControl } from "./components/admin_tools/access-control/AccessControl.tsx"
import { NotImplemented } from "./pages/NotImplemented.tsx";
import { SessionsPage } from "./pages/SessionsPage.tsx";
import Services from "./pages/Services.tsx";
Expand Down Expand Up @@ -76,6 +77,7 @@ const router = createBrowserRouter(
<Route path="variables/secured" element={<SecuredVariables />} />
<Route path="audit" element={<ActionsLog />} />
<Route path="sessions" element={<SessionsPage />} />
<Route path="access-control" element={<AccessControl />} />
<Route path="exchanges" element={<LiveExchanges />} />
</Route>
<Route index path="/" element={<Navigate to="/chains" />} />
Expand Down
16 changes: 16 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ import type {
VariableImportPreview,
SecretWithVariables,
Variable,
AccessControlSearchRequest,
AccessControlResponse,
AccessControlUpdateRequest,
AccessControlBulkDeployRequest,
} from "./apiTypes.ts";
import { RestApi } from "./rest/restApi.ts";
import { isVsCode, VSCodeExtensionApi } from "./rest/vscodeExtensionApi.ts";
Expand Down Expand Up @@ -501,6 +505,18 @@ export interface Api {
): Promise<ApiResponse<boolean>>;
createSecret(secretName: string): Promise<ApiResponse<boolean>>;
downloadHelmChart(secretName: string): Promise<File>;

loadHttpTriggerAccessControl(
searchRequest: AccessControlSearchRequest,
): Promise<AccessControlResponse>;

updateHttpTriggerAccessControl(
searchRequest: AccessControlUpdateRequest[],
): Promise<AccessControlResponse>;

bulkDeployChainsAccessControl(
searchRequest: AccessControlBulkDeployRequest[],
): Promise<AccessControlResponse>;
}

export const api: Api = isVsCode ? new VSCodeExtensionApi() : new RestApi();
57 changes: 57 additions & 0 deletions src/api/apiTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,63 @@ export interface SpecApiFile {
fileUri: string;
}

export type AccessControlSearchRequest = {
offset: number;
limit: number;
filters: unknown;
};

export type AccessControlUpdateRequest = {
elementId: string;
isRedeploy: boolean;
roles: string[];
};

export type AccessControlResponse = {
offset: number;
roles: AccessControl[];
};

export type AccessControlBulkDeployRequest = {
chainId: string;
unsavedChanges: boolean;
};

export type AccessControl = {
chainId: string;
chainName: string;
elementId: string;
elementName: string;
deploymentStatus: string[];
unsavedChanges: boolean;
properties: Record<string, AccessControlProperty>;
modifiedWhen: number;
};

export enum AccessControlType {
RBAC = "RBAC",
ABAC = "ABAC",
NONE = "NONE",
}

export type AbacParameters = {
operation: string;
resourceType: string;
resourceDataType: string;
resourceString?: string;
resourceMap?: Record<string, unknown>;
};

export type AccessControlProperty = {
roles: string[];
contextPath?: string;
integrationOperationPath?: string;
externalRoute: boolean;
privateRoute: boolean;
accessControlType?: AccessControlType;
abacParameters?: AbacParameters;
};

export type LiveExchange = {
exchangeId: string;
deploymentId: string;
Expand Down
36 changes: 36 additions & 0 deletions src/api/rest/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ import {
BulkDeploymentResult,
ImportVariablesResult,
VariableImportPreview,
AccessControlSearchRequest,
AccessControlResponse,
AccessControlUpdateRequest,
AccessControlBulkDeployRequest,
} from "../apiTypes.ts";
import { Api } from "../api.ts";
import { getFileFromResponse } from "../../misc/download-utils.ts";
Expand Down Expand Up @@ -1880,4 +1884,36 @@ export class RestApi implements Api {
);
return response.data;
};

loadHttpTriggerAccessControl = async (
searchRequest: AccessControlSearchRequest,
): Promise<AccessControlResponse> => {
const response = await this.instance.post<AccessControlResponse>(
`${this.v1()}/catalog/chains/roles`,
searchRequest,
);
return response.data;
};

updateHttpTriggerAccessControl = async (
searchRequest: AccessControlUpdateRequest[],
): Promise<AccessControlResponse> => {
const response = await this.instance.put<AccessControlResponse>(
`${this.v1()}/catalog/chains/roles`,
searchRequest,
);

return response.data;
};

bulkDeployChainsAccessControl = async (
searchRequest: AccessControlBulkDeployRequest[],
): Promise<AccessControlResponse> => {
const response = await this.instance.put<AccessControlResponse>(
`${this.v1()}/catalog/chains/roles/redeploy`,
searchRequest,
);

return response.data;
};
}
13 changes: 13 additions & 0 deletions src/api/rest/vscodeExtensionApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
ActionDifference,
ActionLogResponse,
AccessControlResponse,
BaseEntity,
Chain,
ChainDeployment,
Expand Down Expand Up @@ -949,6 +950,18 @@ export class VSCodeExtensionApi implements Api {
throw new Error("Method bulkDeploy not implemented.");
}

loadHttpTriggerAccessControl = async (): Promise<AccessControlResponse> => {
throw new Error("Method loadHttpTriggerAccessControl not implemented.");
};

updateHttpTriggerAccessControl = async (): Promise<AccessControlResponse> => {
throw new Error("Method updateHttpTriggerAccessControl not implemented.");
};

bulkDeployChainsAccessControl = async (): Promise<AccessControlResponse> => {
throw new Error("Method bulkDeployChainsAccessControl not implemented.");
};

getCommonVariables(): Promise<ApiResponse<Variable[]>> {
throw new RestApiError("Not implemented", 501);
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/admin_tools/AdminToolsSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ const menuItems = [
label: "Sessions",
},
{
key: "/admintools/roles",
key: "/admintools/access-control",
icon: <OverridableIcon name="settings" />,
label: "Roles",
label: "Access Control",
},
{
key: "/admintools/design-templates",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.header {
display: flex;
align-items: center;
margin-bottom: 8px;
}

.leftHeader {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}

.iconWrapper {
display: flex;
align-items: center;
gap: 8px;
}

.badge {
min-width: 22px;
height: 22px;
border-radius: 11px;
background: #e6eef8;
color: #0b66ff;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
}

.table {
width: 100%;
border-collapse: collapse;
}

.th {
text-align: left;
border-bottom: 1px solid #ddd;
padding: 8px 4px;
font-weight: 500;
}

.td {
border-bottom: 1px solid #eee;
padding: 4px;
}

.noEntries {
font-weight: 600;
}
118 changes: 118 additions & 0 deletions src/components/admin_tools/access-control/AbacAttributesPopUp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Button, Form, Input, Modal } from "antd";
import React, { useState } from "react";
import { useModalContext } from "../../../ModalContextProvider.tsx";
import {
AccessControl as AccessControlData,
AccessControlProperty,
} from "../../../api/apiTypes.ts";
import { OverridableIcon } from "../../../icons/IconProvider.tsx";
import styles from "./AbacAttributesPopUp.module.css";

export type AbacAttributesPopUpProps = {
record: AccessControlData;
};

export const AbacAttributesPopUp: React.FC<AbacAttributesPopUpProps> = ({
record,
}) => {
const { closeContainingModal } = useModalContext();
const props = record.properties as unknown as
| AccessControlProperty
| undefined;
const abac = props?.abacParameters;

return (
<Modal
title="ABAC Parameters"
open={true}
onCancel={closeContainingModal}
footer={[
<Button key="close" onClick={closeContainingModal}>
Close
</Button>,
]}
width={600}
>
<Form
layout="vertical"
disabled
labelCol={{ flex: "23px" }}
wrapperCol={{ flex: "auto" }}
labelWrap
>
<Form.Item label="Resource Type">
<Input value={abac?.resourceType ?? ""} placeholder="—" />
</Form.Item>
<Form.Item label="Operation">
<Input value={abac?.operation ?? ""} placeholder="—" />
</Form.Item>
<Form.Item label="Resource Data Type">
<Input value={abac?.resourceDataType ?? ""} placeholder="—" />
</Form.Item>
{abac?.resourceMap && (
<Form.Item>
<ResourceMapDisplay resourceMap={abac.resourceMap} />
</Form.Item>
)}
{abac?.resourceString != null && abac.resourceString !== "" && (
<Form.Item label="Resource String">
<Input value={abac.resourceString} placeholder="—" />
</Form.Item>
)}
</Form>
</Modal>
);
};

const ResourceMapDisplay: React.FC<{
resourceMap: Record<string, unknown>;
}> = ({ resourceMap }) => {
const rowCount = Object.entries(resourceMap).length;
const [collapsed, setCollapsed] = useState(false);

return (
<div>
<div className={styles.header}>
<div
className={styles.leftHeader}
onClick={() => setCollapsed((s) => !s)}
>
<span className={styles.iconWrapper}>
{collapsed ? (
<OverridableIcon name="right" />
) : (
<OverridableIcon name="down" />
)}
</span>
<span>Resource Map</span>
<span className={styles.badge}>{rowCount}</span>
</div>
</div>
{!collapsed &&
(rowCount === 0 ? (
<div className={styles.noEntries}>No entries.</div>
) : (
<table className={styles.table}>
<thead>
<tr>
<th className={styles.th}>Name</th>
<th className={styles.th}>Value</th>
</tr>
</thead>
<tbody>
{Object.entries(resourceMap).map(([key, value], idx) => (
<tr key={idx}>
<td className={styles.td}>
<Input value={key} disabled placeholder="Name" />
</td>
<td className={styles.td}>
<Input value={String(value)} disabled placeholder="Value" />
</td>
</tr>
))}
</tbody>
</table>
))}
</div>
);
};
Loading
Loading