mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-18 14:55:22 +00:00
🚧 wip
This commit is contained in:
@@ -238,6 +238,8 @@
|
|||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"resourceSettingDescription": "Configure the settings on the resource",
|
"resourceSettingDescription": "Configure the settings on the resource",
|
||||||
"resourceSetting": "{resourceName} Settings",
|
"resourceSetting": "{resourceName} Settings",
|
||||||
|
"resourcePolicySettingDescription": "Configure the settings on the resource policy",
|
||||||
|
"resourcePolicySetting": "{policyName} Settings",
|
||||||
"alwaysAllow": "Bypass Auth",
|
"alwaysAllow": "Bypass Auth",
|
||||||
"alwaysDeny": "Block Access",
|
"alwaysDeny": "Block Access",
|
||||||
"passToAuth": "Pass to Auth",
|
"passToAuth": "Pass to Auth",
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ export enum ActionsEnum {
|
|||||||
listApprovals = "listApprovals",
|
listApprovals = "listApprovals",
|
||||||
updateApprovals = "updateApprovals",
|
updateApprovals = "updateApprovals",
|
||||||
listResourcePolicies = "listResourcePolicies",
|
listResourcePolicies = "listResourcePolicies",
|
||||||
|
getResourcePolicy = "getResourcePolicy",
|
||||||
createResourcePolicy = "createResourcePolicy",
|
createResourcePolicy = "createResourcePolicy",
|
||||||
updateResourcePolicy = "updateResourcePolicy",
|
updateResourcePolicy = "updateResourcePolicy",
|
||||||
deleteResourcePolicy = "deleteResourcePolicy",
|
deleteResourcePolicy = "deleteResourcePolicy",
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ export * from "./verifyApiKeyApiKeyAccess";
|
|||||||
export * from "./verifyApiKeyClientAccess";
|
export * from "./verifyApiKeyClientAccess";
|
||||||
export * from "./verifyApiKeySiteResourceAccess";
|
export * from "./verifyApiKeySiteResourceAccess";
|
||||||
export * from "./verifyApiKeyIdpAccess";
|
export * from "./verifyApiKeyIdpAccess";
|
||||||
|
export * from "./verifyApiKeyResourcePolicyAccess";
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { db } from "@server/db";
|
||||||
|
import { resourcePolicies, apiKeyOrg } from "@server/db";
|
||||||
|
import { eq, and } from "drizzle-orm";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
||||||
|
export async function verifyApiKeyResourcePolicyAccess(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const apiKey = req.apiKey;
|
||||||
|
const resourcePolicyId =
|
||||||
|
req.params.resourcePolicyId ||
|
||||||
|
req.body.resourcePolicyId ||
|
||||||
|
req.query.resourcePolicyId;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.UNAUTHORIZED, "Key not authenticated")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Retrieve the resource policy
|
||||||
|
const [policy] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourcePolicies)
|
||||||
|
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!policy) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Resource policy with ID ${resourcePolicyId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey.isRoot) {
|
||||||
|
// Root keys can access any resource policy in any org
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!policy.orgId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
`Resource policy with ID ${resourcePolicyId} does not have an organization ID`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the API key is linked to the resource policy's organization
|
||||||
|
if (!req.apiKeyOrg) {
|
||||||
|
const apiKeyOrgResult = await db
|
||||||
|
.select()
|
||||||
|
.from(apiKeyOrg)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(apiKeyOrg.apiKeyId, apiKey.apiKeyId),
|
||||||
|
eq(apiKeyOrg.orgId, policy.orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (apiKeyOrgResult.length > 0) {
|
||||||
|
req.apiKeyOrg = apiKeyOrgResult[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.apiKeyOrg) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
"Key does not have access to this organization"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
} catch (error) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Error verifying resource policy access"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
resourcePolicies,
|
resourcePolicies,
|
||||||
rolePolicies,
|
rolePolicies,
|
||||||
userPolicies,
|
userPolicies,
|
||||||
type ResourcePolicy,
|
|
||||||
type ResourcePolicy
|
type ResourcePolicy
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import config from "@server/lib/config";
|
|||||||
import * as site from "./site";
|
import * as site from "./site";
|
||||||
import * as org from "./org";
|
import * as org from "./org";
|
||||||
import * as resource from "./resource";
|
import * as resource from "./resource";
|
||||||
|
import * as policy from "./policy";
|
||||||
import * as domain from "./domain";
|
import * as domain from "./domain";
|
||||||
import * as target from "./target";
|
import * as target from "./target";
|
||||||
import * as user from "./user";
|
import * as user from "./user";
|
||||||
@@ -521,6 +522,7 @@ authenticated.get(
|
|||||||
verifyUserHasAction(ActionsEnum.getResource),
|
verifyUserHasAction(ActionsEnum.getResource),
|
||||||
resource.getResource
|
resource.getResource
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.post(
|
authenticated.post(
|
||||||
"/resource/:resourceId",
|
"/resource/:resourceId",
|
||||||
verifyResourceAccess,
|
verifyResourceAccess,
|
||||||
@@ -627,6 +629,15 @@ authenticated.post(
|
|||||||
logActionAudit(ActionsEnum.updateRole),
|
logActionAudit(ActionsEnum.updateRole),
|
||||||
role.updateRole
|
role.updateRole
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/resource-policy/:niceId",
|
||||||
|
verifyOrgAccess,
|
||||||
|
verifyResourcePolicyAccess,
|
||||||
|
verifyUserHasAction(ActionsEnum.getResourcePolicy),
|
||||||
|
policy.getResourcePolicy
|
||||||
|
);
|
||||||
|
|
||||||
// authenticated.get(
|
// authenticated.get(
|
||||||
// "/role/:roleId",
|
// "/role/:roleId",
|
||||||
// verifyRoleAccess,
|
// verifyRoleAccess,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as site from "./site";
|
|||||||
import * as org from "./org";
|
import * as org from "./org";
|
||||||
import * as blueprints from "./blueprints";
|
import * as blueprints from "./blueprints";
|
||||||
import * as resource from "./resource";
|
import * as resource from "./resource";
|
||||||
|
import * as policy from "./policy";
|
||||||
import * as domain from "./domain";
|
import * as domain from "./domain";
|
||||||
import * as target from "./target";
|
import * as target from "./target";
|
||||||
import * as user from "./user";
|
import * as user from "./user";
|
||||||
@@ -27,7 +28,8 @@ import {
|
|||||||
verifyApiKeyClientAccess,
|
verifyApiKeyClientAccess,
|
||||||
verifyApiKeySiteResourceAccess,
|
verifyApiKeySiteResourceAccess,
|
||||||
verifyApiKeySetResourceClients,
|
verifyApiKeySetResourceClients,
|
||||||
verifyLimits
|
verifyLimits,
|
||||||
|
verifyApiKeyResourcePolicyAccess
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
@@ -392,6 +394,13 @@ authenticated.get(
|
|||||||
resource.getResource
|
resource.getResource
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/resource-policy/:resourcePolicyId",
|
||||||
|
verifyApiKeyResourcePolicyAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.getResourcePolicy),
|
||||||
|
policy.getResourcePolicy
|
||||||
|
);
|
||||||
|
|
||||||
authenticated.post(
|
authenticated.post(
|
||||||
"/resource/:resourceId",
|
"/resource/:resourceId",
|
||||||
verifyApiKeyResourceAccess,
|
verifyApiKeyResourceAccess,
|
||||||
|
|||||||
123
server/routers/policy/getResourcePolicy.ts
Normal file
123
server/routers/policy/getResourcePolicy.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { db, resourcePolicies, rolePolicies, userPolicies } from "@server/db";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import { and, eq, type SQL } from "drizzle-orm";
|
||||||
|
import type { NextFunction, Request, Response } from "express";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import z from "zod";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
|
const getResourcePolicySchema = z
|
||||||
|
.strictObject({
|
||||||
|
niceId: z.string(),
|
||||||
|
orgId: z.string()
|
||||||
|
})
|
||||||
|
.or(
|
||||||
|
z.strictObject({
|
||||||
|
resourcePolicyId: z.coerce.number<string>().int().positive()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
async function query(params: z.infer<typeof getResourcePolicySchema>) {
|
||||||
|
const conditions: SQL<unknown>[] = [];
|
||||||
|
if ("resourcePolicyId" in params) {
|
||||||
|
conditions.push(
|
||||||
|
eq(resourcePolicies.resourcePolicyId, params.resourcePolicyId)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
conditions.push(
|
||||||
|
eq(resourcePolicies.niceId, params.niceId),
|
||||||
|
eq(resourcePolicies.orgId, params.orgId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [res] = await db
|
||||||
|
.select({
|
||||||
|
policy: resourcePolicies,
|
||||||
|
userPolicies,
|
||||||
|
rolePolicies
|
||||||
|
})
|
||||||
|
.from(resourcePolicies)
|
||||||
|
.leftJoin(
|
||||||
|
userPolicies,
|
||||||
|
eq(userPolicies.resourcePolicyId, resourcePolicies.resourcePolicyId)
|
||||||
|
)
|
||||||
|
.leftJoin(
|
||||||
|
rolePolicies,
|
||||||
|
eq(rolePolicies.resourcePolicyId, resourcePolicies.resourcePolicyId)
|
||||||
|
)
|
||||||
|
.where(and(...conditions))
|
||||||
|
.limit(1);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetResourcePolicyResponse = Awaited<ReturnType<typeof query>>;
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/org/{orgId}/resource-policy/{niceId}",
|
||||||
|
description:
|
||||||
|
"Get a resource policy by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.",
|
||||||
|
tags: [OpenAPITags.Org, OpenAPITags.Policy],
|
||||||
|
request: {
|
||||||
|
params: z.object({
|
||||||
|
orgId: z.string(),
|
||||||
|
niceId: z.string()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/resource-policy/{resourcePolicyId}",
|
||||||
|
description: "Get a resource policy by its resourcePolicyId.",
|
||||||
|
tags: [OpenAPITags.Policy],
|
||||||
|
request: {
|
||||||
|
params: z.object({
|
||||||
|
resourcePolicyId: z.number()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function getResourcePolicy(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const parsedParams = getResourcePolicySchema.safeParse(req.params);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedParams.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const policy = await query(parsedParams.data);
|
||||||
|
|
||||||
|
if (!policy) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.NOT_FOUND, "Resource policy not found")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response<GetResourcePolicyResponse>(res, {
|
||||||
|
data: policy,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Resource Policy retrieved successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
server/routers/policy/index.ts
Normal file
1
server/routers/policy/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./getResourcePolicy";
|
||||||
23
src/app/[orgId]/settings/(private)/policies/layout.tsx
Normal file
23
src/app/[orgId]/settings/(private)/policies/layout.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
||||||
|
import OrgProvider from "@app/providers/OrgProvider";
|
||||||
|
import type { GetOrgResponse } from "@server/routers/org";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export interface PolicyLayoutPageProps {
|
||||||
|
params: Promise<{ orgId: string }>;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PolicyLayoutPage(props: PolicyLayoutPageProps) {
|
||||||
|
const params = await props.params;
|
||||||
|
|
||||||
|
let org: GetOrgResponse | null = null;
|
||||||
|
try {
|
||||||
|
const res = await getCachedOrg(params.orgId);
|
||||||
|
org = res.data.data;
|
||||||
|
} catch {
|
||||||
|
redirect(`/${params.orgId}/settings`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <OrgProvider org={org}>{props.children}</OrgProvider>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { EditPolicyForm } from "@app/components/resource-policy/EditPolicyForm";
|
||||||
|
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
|
import { Button } from "@app/components/ui/button";
|
||||||
|
import { internal } from "@app/lib/api";
|
||||||
|
import { authCookieHeader } from "@app/lib/api/cookies";
|
||||||
|
import type { ResourcePolicy } from "@server/db";
|
||||||
|
import type { GetResourcePolicyResponse } from "@server/routers/policy";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export interface EditPolicyPageProps {
|
||||||
|
params: Promise<{ niceId: string; orgId: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function EditPolicyPage(props: EditPolicyPageProps) {
|
||||||
|
const params = await props.params;
|
||||||
|
const t = await getTranslations();
|
||||||
|
|
||||||
|
let policy: ResourcePolicy | null = null;
|
||||||
|
try {
|
||||||
|
const res = await internal.get<
|
||||||
|
AxiosResponse<GetResourcePolicyResponse>
|
||||||
|
>(
|
||||||
|
`/org/${params.orgId}/resource-policy/${params.niceId}`,
|
||||||
|
await authCookieHeader()
|
||||||
|
);
|
||||||
|
policy = res.data.data.policy;
|
||||||
|
} catch {
|
||||||
|
redirect(`/${params.orgId}/settings/policies/resource`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!policy) {
|
||||||
|
redirect(`/${params.orgId}/settings/policies/resource`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<SettingsSectionTitle
|
||||||
|
title={t("resourcePolicySetting", {
|
||||||
|
policyName: policy.name
|
||||||
|
})}
|
||||||
|
description={t("resourcePolicySettingDescription")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button asChild variant="outline">
|
||||||
|
<Link href={`/${params.orgId}/settings/policies/resource`}>
|
||||||
|
{t("resourcePoliciesSeeAll")}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditPolicyForm policy={policy} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
import { CreatePolicyForm } from "@app/components/resource-policy/CreatePolicyForm";
|
import { CreatePolicyForm } from "@app/components/resource-policy/CreatePolicyForm";
|
||||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
|
|
||||||
import OrgProvider from "@app/providers/OrgProvider";
|
|
||||||
import type { GetOrgResponse } from "@server/routers/org";
|
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
|
|
||||||
export interface CreateResourcePolicyPageProps {
|
export interface CreateResourcePolicyPageProps {
|
||||||
params: Promise<{ orgId: string }>;
|
params: Promise<{ orgId: string }>;
|
||||||
@@ -18,13 +14,6 @@ export default async function CreateResourcePolicyPage(
|
|||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const t = await getTranslations();
|
const t = await getTranslations();
|
||||||
|
|
||||||
let org: GetOrgResponse | null = null;
|
|
||||||
try {
|
|
||||||
const res = await getCachedOrg(params.orgId);
|
|
||||||
org = res.data.data;
|
|
||||||
} catch {
|
|
||||||
redirect(`/${params.orgId}/settings/resources`);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
@@ -34,15 +23,13 @@ export default async function CreateResourcePolicyPage(
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Button asChild variant="outline">
|
<Button asChild variant="outline">
|
||||||
<Link href={`/${params.orgId}/settings/resources/policies`}>
|
<Link href={`/${params.orgId}/settings/policies/resource`}>
|
||||||
{t("resourcePoliciesSeeAll")}
|
{t("resourcePoliciesSeeAll")}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OrgProvider org={org}>
|
<CreatePolicyForm />
|
||||||
<CreatePolicyForm />
|
|
||||||
</OrgProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -55,17 +55,15 @@ export default async function ResourcePoliciesPage(
|
|||||||
description={t("resourcePoliciesDescription")}
|
description={t("resourcePoliciesDescription")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<OrgProvider org={org}>
|
<ResourcePoliciesTable
|
||||||
<ResourcePoliciesTable
|
policies={policies}
|
||||||
policies={policies}
|
orgId={params.orgId}
|
||||||
orgId={params.orgId}
|
rowCount={pagination.total}
|
||||||
rowCount={pagination.total}
|
pagination={{
|
||||||
pagination={{
|
pageIndex: pagination.page - 1,
|
||||||
pageIndex: pagination.page - 1,
|
pageSize: pagination.pageSize
|
||||||
pageSize: pagination.pageSize
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</OrgProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "sidebarResourcePolicies",
|
title: "sidebarResourcePolicies",
|
||||||
href: "/{orgId}/settings/policies/resources",
|
href: "/{orgId}/settings/policies/resource",
|
||||||
icon: (
|
icon: (
|
||||||
<GlobeIcon className="size-4 flex-none" />
|
<GlobeIcon className="size-4 flex-none" />
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export function ResourcePoliciesTable({
|
|||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<Link
|
<Link
|
||||||
className="block w-full"
|
className="block w-full"
|
||||||
href={`/${policyRow.orgId}/settings/resources/proxy/${policyRow.niceId}`}
|
href={`/${policyRow.orgId}/settings/policies/resource/${policyRow.niceId}`}
|
||||||
>
|
>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
{t("viewSettings")}
|
{t("viewSettings")}
|
||||||
@@ -122,7 +122,7 @@ export function ResourcePoliciesTable({
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<Link
|
<Link
|
||||||
href={`/${policyRow.orgId}/settings/resources/proxy/${policyRow.niceId}`}
|
href={`/${policyRow.orgId}/settings/policies/resource/${policyRow.niceId}`}
|
||||||
>
|
>
|
||||||
<Button variant={"outline"}>
|
<Button variant={"outline"}>
|
||||||
{t("edit")}
|
{t("edit")}
|
||||||
@@ -165,7 +165,7 @@ export function ResourcePoliciesTable({
|
|||||||
onAdd={() =>
|
onAdd={() =>
|
||||||
startNavigation(() =>
|
startNavigation(() =>
|
||||||
router.push(
|
router.push(
|
||||||
`/${orgId}/settings/policies/resources/create`
|
`/${orgId}/settings/policies/resource/create`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,62 @@ type LocalRule = {
|
|||||||
updated?: boolean;
|
updated?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ─── PolicyNameSection ──────────────────────────────────────────────────
|
||||||
|
type PolicyNameSectionProps = {
|
||||||
|
form: UseFormReturn<PolicyFormValues, any, any>;
|
||||||
|
isEditing?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function PolicyNameSection({ form }: PolicyNameSectionProps) {
|
||||||
|
const t = useTranslations();
|
||||||
|
return (
|
||||||
|
<SettingsSection>
|
||||||
|
<SettingsSectionHeader>
|
||||||
|
<SettingsSectionTitle>
|
||||||
|
{t("resourcePolicyName")}
|
||||||
|
</SettingsSectionTitle>
|
||||||
|
<SettingsSectionDescription>
|
||||||
|
{t("resourcePolicyNameDescription")}
|
||||||
|
</SettingsSectionDescription>
|
||||||
|
</SettingsSectionHeader>
|
||||||
|
<SettingsSectionBody>
|
||||||
|
<SettingsSectionForm>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("name")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder={t(
|
||||||
|
"resourcePolicyNamePlaceholder"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SettingsSectionForm>
|
||||||
|
</SettingsSectionBody>
|
||||||
|
|
||||||
|
<div className="flex py-6 justify-end">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
// loading={isSubmitting}
|
||||||
|
// disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{t("resourcePoliciesCreate")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</SettingsSection>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── PolicyUsersRolesSection ──────────────────────────────────────────────────
|
||||||
|
|
||||||
type PolicyUsersRolesSectionProps = {
|
type PolicyUsersRolesSectionProps = {
|
||||||
form: UseFormReturn<PolicyFormValues, any, any>;
|
form: UseFormReturn<PolicyFormValues, any, any>;
|
||||||
allRoles: { id: string; text: string }[];
|
allRoles: { id: string; text: string }[];
|
||||||
@@ -128,8 +184,6 @@ type PolicyUsersRolesSectionProps = {
|
|||||||
allIdps: { id: number; text: string }[];
|
allIdps: { id: number; text: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─── PolicyUsersRolesSection ──────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export function PolicyUsersRolesSection({
|
export function PolicyUsersRolesSection({
|
||||||
form,
|
form,
|
||||||
allRoles,
|
allRoles,
|
||||||
|
|||||||
@@ -45,3 +45,21 @@ export const createPolicySchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type PolicyFormValues = z.infer<typeof createPolicySchema>;
|
export type PolicyFormValues = z.infer<typeof createPolicySchema>;
|
||||||
|
|
||||||
|
export const addRuleSchema = z.object({
|
||||||
|
action: z.enum(["ACCEPT", "DROP", "PASS"]),
|
||||||
|
match: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
priority: z.coerce.number<number>().int().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LocalRule = {
|
||||||
|
ruleId: number;
|
||||||
|
action: "ACCEPT" | "DROP" | "PASS";
|
||||||
|
match: string;
|
||||||
|
value: string;
|
||||||
|
priority: number;
|
||||||
|
enabled: boolean;
|
||||||
|
new?: boolean;
|
||||||
|
updated?: boolean;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user