mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-19 23:35:26 +00:00
🚧 wip
This commit is contained in:
@@ -1142,3 +1142,5 @@ export type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
|
|||||||
export type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
|
export type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
|
||||||
export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
|
export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
|
||||||
export type ResourcePolicy = InferSelectModel<typeof resourcePolicies>;
|
export type ResourcePolicy = InferSelectModel<typeof resourcePolicies>;
|
||||||
|
export type RolePolicy = InferSelectModel<typeof rolePolicies>;
|
||||||
|
export type UserPolicy = InferSelectModel<typeof userPolicies>;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export enum OpenAPITags {
|
|||||||
Site = "Site",
|
Site = "Site",
|
||||||
Org = "Organization",
|
Org = "Organization",
|
||||||
Resource = "Resource",
|
Resource = "Resource",
|
||||||
|
Policy = "Policy",
|
||||||
Role = "Role",
|
Role = "Role",
|
||||||
User = "User",
|
User = "User",
|
||||||
Invitation = "Invitation",
|
Invitation = "Invitation",
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import {
|
|||||||
resourcePolicies,
|
resourcePolicies,
|
||||||
rolePolicies,
|
rolePolicies,
|
||||||
roles,
|
roles,
|
||||||
|
userOrgs,
|
||||||
userPolicies,
|
userPolicies,
|
||||||
|
users,
|
||||||
type ResourcePolicy
|
type ResourcePolicy
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq, inArray, not, type InferInsertModel } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { getUniqueResourcePolicyName } from "@server/db/names";
|
import { getUniqueResourcePolicyName } from "@server/db/names";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
@@ -26,21 +28,24 @@ const createResourcePolicyBodySchema = z.strictObject({
|
|||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
sso: z.boolean(),
|
sso: z.boolean(),
|
||||||
skipToIdpId: z.string().optional(),
|
skipToIdpId: z.string().optional(),
|
||||||
roleIds: z.array(z.string()).optional().default([]),
|
roleIds: z
|
||||||
|
.array(z.string().transform(Number).pipe(z.int().positive()))
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
userIds: z.array(z.string()).optional().default([])
|
userIds: z.array(z.string()).optional().default([])
|
||||||
});
|
});
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "post",
|
method: "post",
|
||||||
path: "/org/{orgId}/resource-policy",
|
path: "/org/{orgId}/resource-policy",
|
||||||
description: "Create a resource.",
|
description: "Create a resource policy.",
|
||||||
tags: [OpenAPITags.Org, OpenAPITags.Resource],
|
tags: [OpenAPITags.Org, OpenAPITags.Policy],
|
||||||
request: {
|
request: {
|
||||||
params: createResourcePolicyParamsSchema,
|
params: createResourcePolicyParamsSchema,
|
||||||
body: {
|
body: {
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: createResourcePolicyParamsSchema
|
schema: createResourcePolicyBodySchema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,6 +130,28 @@ export async function createResourcePolicy(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingRoles = await db
|
||||||
|
.select()
|
||||||
|
.from(roles)
|
||||||
|
.where(and(inArray(roles.roleId, roleIds)));
|
||||||
|
|
||||||
|
const hasAdminRole = existingRoles.some((role) => role.isAdmin);
|
||||||
|
|
||||||
|
if (hasAdminRole) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Admin role cannot be assigned to resource policy"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUsers = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.innerJoin(userOrgs, eq(userOrgs.userId, users.userId))
|
||||||
|
.where(and(inArray(users.userId, userIds)));
|
||||||
|
|
||||||
const niceId = await getUniqueResourcePolicyName(orgId);
|
const niceId = await getUniqueResourcePolicyName(orgId);
|
||||||
|
|
||||||
const policy = await db.transaction(async (trx) => {
|
const policy = await db.transaction(async (trx) => {
|
||||||
@@ -138,19 +165,43 @@ export async function createResourcePolicy(
|
|||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
await trx.insert(rolePolicies).values({
|
const rolesToAdd = [
|
||||||
roleId: adminRole[0].roleId,
|
{
|
||||||
resourcePolicyId: newPolicy.resourcePolicyId
|
roleId: adminRole[0].roleId,
|
||||||
});
|
resourcePolicyId: newPolicy.resourcePolicyId
|
||||||
|
}
|
||||||
|
] satisfies InferInsertModel<typeof rolePolicies>[];
|
||||||
|
|
||||||
|
rolesToAdd.push(
|
||||||
|
...existingRoles.map((role) => ({
|
||||||
|
roleId: role.roleId,
|
||||||
|
resourcePolicyId: newPolicy.resourcePolicyId
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
await trx.insert(rolePolicies).values(rolesToAdd);
|
||||||
|
|
||||||
|
const usersToAdd: InferInsertModel<typeof userPolicies>[] = [];
|
||||||
|
|
||||||
if (req.user && req.userOrgRoleId != adminRole[0].roleId) {
|
if (req.user && req.userOrgRoleId != adminRole[0].roleId) {
|
||||||
// make sure the user can access the policy
|
// make sure the user can access the policy
|
||||||
await trx.insert(userPolicies).values({
|
usersToAdd.push({
|
||||||
userId: req.user?.userId!,
|
userId: req.user?.userId!,
|
||||||
resourcePolicyId: newPolicy.resourcePolicyId
|
resourcePolicyId: newPolicy.resourcePolicyId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usersToAdd.push(
|
||||||
|
...existingUsers.map(({ user }) => ({
|
||||||
|
userId: user.userId,
|
||||||
|
resourcePolicyId: newPolicy.resourcePolicyId
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (usersToAdd.length > 0) {
|
||||||
|
await trx.insert(userPolicies).values(usersToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
return newPolicy;
|
return newPolicy;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
165
server/private/routers/policy/updateResourcePolicy.ts
Normal file
165
server/private/routers/policy/updateResourcePolicy.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import z from "zod";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import {
|
||||||
|
db,
|
||||||
|
orgs,
|
||||||
|
resourcePolicies,
|
||||||
|
rolePolicies,
|
||||||
|
userPolicies,
|
||||||
|
type ResourcePolicy,
|
||||||
|
type ResourcePolicy
|
||||||
|
} from "@server/db";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
|
||||||
|
const updateResourcePolicyParamsSchema = z.strictObject({
|
||||||
|
resourcePolicyId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateResourcePolicyBodySchema = z.strictObject({
|
||||||
|
name: z.string().min(1).max(255).optional(),
|
||||||
|
niceId: z.string().min(1).max(255).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "put",
|
||||||
|
path: "/resource-policy/{resourcePolicyId}",
|
||||||
|
description: "Update a resource policy.",
|
||||||
|
tags: [OpenAPITags.Org, OpenAPITags.Policy],
|
||||||
|
request: {
|
||||||
|
params: updateResourcePolicyParamsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: updateResourcePolicyBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function updateResourcePolicy(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const parsedParams = updateResourcePolicyParamsSchema.safeParse(
|
||||||
|
req.params
|
||||||
|
);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedParams.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.user && !req.userOrgRoleId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.FORBIDDEN, "User does not have a role")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resourcePolicyId } = parsedParams.data;
|
||||||
|
const [result] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourcePolicies)
|
||||||
|
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId))
|
||||||
|
.leftJoin(orgs, eq(resourcePolicies.orgId, orgs.orgId));
|
||||||
|
|
||||||
|
const policy = result?.resourcePolicies;
|
||||||
|
const org = result?.orgs;
|
||||||
|
|
||||||
|
if (!policy || !org) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Resource Policy with ID ${resourcePolicyId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedBody = updateResourcePolicyBodySchema.safeParse(req.body);
|
||||||
|
if (!parsedBody.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedBody.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = parsedBody.data;
|
||||||
|
|
||||||
|
if (updateData.niceId) {
|
||||||
|
const [existingPolicy] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourcePolicies)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resourcePolicies.niceId, updateData.niceId),
|
||||||
|
eq(resourcePolicies.orgId, policy.orgId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
existingPolicy &&
|
||||||
|
existingPolicy.resourcePolicyId !== policy.resourcePolicyId
|
||||||
|
) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.CONFLICT,
|
||||||
|
`A resource policy with niceId "${updateData.niceId}" already exists`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedPolicy = await db.transaction(async (trx) => {
|
||||||
|
const [updated] = await trx
|
||||||
|
.update(resourcePolicies)
|
||||||
|
.set({
|
||||||
|
...updateData
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
resourcePolicies.resourcePolicyId,
|
||||||
|
policy.resourcePolicyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedPolicy) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to update policy"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response<ResourcePolicy>(res, {
|
||||||
|
data: updatedPolicy,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Resource policy updated successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
263
src/components/resource-policy/EditPolicyForm.tsx
Normal file
263
src/components/resource-policy/EditPolicyForm.tsx
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SettingsContainer,
|
||||||
|
SettingsSection,
|
||||||
|
SettingsSectionBody,
|
||||||
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionForm,
|
||||||
|
SettingsSectionHeader,
|
||||||
|
SettingsSectionTitle
|
||||||
|
} from "@app/components/Settings";
|
||||||
|
import { Button } from "@app/components/ui/button";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage
|
||||||
|
} from "@app/components/ui/form";
|
||||||
|
import { Input } from "@app/components/ui/input";
|
||||||
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
|
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||||
|
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||||
|
|
||||||
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
|
import { orgQueries } from "@app/lib/queries";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { build } from "@server/build";
|
||||||
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
import { UserType } from "@server/types/UserTypes";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
import { useActionState, useMemo } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import z from "zod";
|
||||||
|
import {
|
||||||
|
PolicyAuthMethodsSection,
|
||||||
|
PolicyOtpEmailSection,
|
||||||
|
PolicyRulesSection,
|
||||||
|
PolicyUsersRolesSection
|
||||||
|
} from "./ResourcePolicySubForms";
|
||||||
|
import { type PolicyFormValues, createPolicySchema } from ".";
|
||||||
|
import { toast } from "@app/hooks/useToast";
|
||||||
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
|
import { orgs, type ResourcePolicy } from "@server/db";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
// ─── CreatePolicyForm ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type EditPolicyFormProps = {
|
||||||
|
policy: ResourcePolicy;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EditPolicyForm({}: EditPolicyFormProps) {
|
||||||
|
const { org } = useOrgContext();
|
||||||
|
const t = useTranslations();
|
||||||
|
const { env } = useEnvContext();
|
||||||
|
const api = createApiClient({ env });
|
||||||
|
const [, formAction, isSubmitting] = useActionState(onSubmit, null);
|
||||||
|
const { isPaidUser } = usePaidStatus();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isMaxmindAvailable = !!(
|
||||||
|
env.server.maxmind_db_path && env.server.maxmind_db_path.length > 0
|
||||||
|
);
|
||||||
|
const isMaxmindAsnAvailable = !!(
|
||||||
|
env.server.maxmind_asn_path && env.server.maxmind_asn_path.length > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: orgRoles = [], isLoading: isLoadingOrgRoles } = useQuery(
|
||||||
|
orgQueries.roles({ orgId: org.org.orgId })
|
||||||
|
);
|
||||||
|
const { data: orgUsers = [], isLoading: isLoadingOrgUsers } = useQuery(
|
||||||
|
orgQueries.users({ orgId: org.org.orgId })
|
||||||
|
);
|
||||||
|
const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery(
|
||||||
|
orgQueries.identityProviders({
|
||||||
|
orgId: org.org.orgId,
|
||||||
|
useOrgOnlyIdp: env.app.identityProviderMode === "org"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const form = useForm<PolicyFormValues>({
|
||||||
|
resolver: zodResolver(createPolicySchema) as any,
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
sso: true,
|
||||||
|
skipToIdpId: null,
|
||||||
|
emailWhitelistEnabled: false,
|
||||||
|
roles: [],
|
||||||
|
users: [],
|
||||||
|
emails: [],
|
||||||
|
applyRules: false,
|
||||||
|
rules: [],
|
||||||
|
password: null,
|
||||||
|
headerAuth: null,
|
||||||
|
pincode: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
const isValid = await form.trigger();
|
||||||
|
|
||||||
|
if (!isValid) return;
|
||||||
|
|
||||||
|
const payload = form.getValues();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await api
|
||||||
|
.post<AxiosResponse<ResourcePolicy>>(
|
||||||
|
`/org/${org.org.orgId}/resource-policy/`,
|
||||||
|
{
|
||||||
|
name: payload.name,
|
||||||
|
sso: payload.sso,
|
||||||
|
roleIds: payload.roles.map((r) => r.id),
|
||||||
|
userIds: payload.users.map((u) => u.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch((e) => {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("policyErrorCreate"),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t("policyErrorCreateDescription")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res && res.status === 201) {
|
||||||
|
const id = res.data.data.resourcePolicyId;
|
||||||
|
const niceId = res.data.data.niceId;
|
||||||
|
|
||||||
|
router.push(`/${org.org.orgId}/settings/policies/resources/`);
|
||||||
|
// should redirect to the details page
|
||||||
|
// router.push(
|
||||||
|
// `/${org.org.orgId}/settings/policies/resources/${niceId}`
|
||||||
|
// );
|
||||||
|
toast({
|
||||||
|
title: t("success"),
|
||||||
|
description: t("policyCreatedSuccess")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("policyErrorCreate"),
|
||||||
|
description: t("policyErrorCreateMessageDescription")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allRoles = useMemo(
|
||||||
|
() =>
|
||||||
|
orgRoles
|
||||||
|
.map((role) => ({
|
||||||
|
id: role.roleId.toString(),
|
||||||
|
text: role.name
|
||||||
|
}))
|
||||||
|
.filter((role) => role.text !== "Admin"),
|
||||||
|
[orgRoles]
|
||||||
|
);
|
||||||
|
|
||||||
|
const allUsers = useMemo(
|
||||||
|
() =>
|
||||||
|
orgUsers.map((user) => ({
|
||||||
|
id: user.id.toString(),
|
||||||
|
text: `${getUserDisplayName({ email: user.email, username: user.username })}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}`
|
||||||
|
})),
|
||||||
|
[orgUsers]
|
||||||
|
);
|
||||||
|
|
||||||
|
const allIdps = useMemo(() => {
|
||||||
|
if (build === "saas") {
|
||||||
|
if (isPaidUser(tierMatrix.orgOidc)) {
|
||||||
|
return orgIdps.map((idp) => ({
|
||||||
|
id: idp.idpId,
|
||||||
|
text: idp.name
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return orgIdps.map((idp) => ({ id: idp.idpId, text: idp.name }));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [orgIdps, isPaidUser]);
|
||||||
|
|
||||||
|
if (isLoadingOrgRoles || isLoadingOrgUsers || isLoadingOrgIdps) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form action={formAction}>
|
||||||
|
<SettingsContainer>
|
||||||
|
{/* Name */}
|
||||||
|
<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>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
|
<PolicyUsersRolesSection
|
||||||
|
form={form}
|
||||||
|
allRoles={allRoles}
|
||||||
|
allUsers={allUsers}
|
||||||
|
allIdps={allIdps}
|
||||||
|
/>
|
||||||
|
<PolicyAuthMethodsSection form={form} />
|
||||||
|
<PolicyOtpEmailSection
|
||||||
|
form={form}
|
||||||
|
emailEnabled={env.email.emailEnabled}
|
||||||
|
/>
|
||||||
|
<PolicyRulesSection
|
||||||
|
form={form}
|
||||||
|
isMaxmindAvailable={isMaxmindAvailable}
|
||||||
|
isMaxmindAsnAvailable={isMaxmindAsnAvailable}
|
||||||
|
/>
|
||||||
|
</SettingsContainer>
|
||||||
|
|
||||||
|
<div className="flex py-6 justify-end">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
loading={isSubmitting}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{t("resourcePoliciesCreate")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user