apply rules on resource policies

This commit is contained in:
Fred KISSIE
2026-03-05 18:13:30 +01:00
parent 8a3c0d9a08
commit de2980e1bc
4 changed files with 90 additions and 16 deletions

View File

@@ -661,7 +661,7 @@ export const resourceRules = pgTable("resourceRules", {
value: varchar("value").notNull() value: varchar("value").notNull()
}); });
export const policyRules = pgTable("policyRules", { export const resourcePolicyRules = pgTable("resourcePolicyRules", {
ruleId: serial("ruleId").primaryKey(), ruleId: serial("ruleId").primaryKey(),
resourcePolicyId: integer("resourcePolicyId") resourcePolicyId: integer("resourcePolicyId")
.notNull() .notNull()

View File

@@ -1,6 +1,7 @@
import { import {
db, db,
idp, idp,
resourcePolicyRules,
resourcePolicies, resourcePolicies,
resourcePolicyHeaderAuth, resourcePolicyHeaderAuth,
resourcePolicyPassword, resourcePolicyPassword,
@@ -56,6 +57,7 @@ async function query(params: z.infer<typeof getResourcePolicySchema>) {
.select({ .select({
resourcePolicyId: resourcePolicies.resourcePolicyId, resourcePolicyId: resourcePolicies.resourcePolicyId,
sso: resourcePolicies.sso, sso: resourcePolicies.sso,
applyRules: resourcePolicies.applyRules,
emailWhitelistEnabled: resourcePolicies.emailWhitelistEnabled, emailWhitelistEnabled: resourcePolicies.emailWhitelistEnabled,
idpId: resourcePolicies.idpId, idpId: resourcePolicies.idpId,
niceId: resourcePolicies.niceId, niceId: resourcePolicies.niceId,
@@ -134,11 +136,24 @@ async function query(params: z.infer<typeof getResourcePolicySchema>) {
eq(resourcePolicyWhiteList.resourcePolicyId, res.resourcePolicyId) eq(resourcePolicyWhiteList.resourcePolicyId, res.resourcePolicyId)
); );
const policyRules = await db
.select({
ruleId: resourcePolicyRules.ruleId,
enabled: resourcePolicyRules.enabled,
priority: resourcePolicyRules.priority,
action: resourcePolicyRules.action,
match: resourcePolicyRules.match,
value: resourcePolicyRules.value
})
.from(resourcePolicyRules)
.where(eq(resourcePolicyRules.resourcePolicyId, res.resourcePolicyId));
return { return {
...res, ...res,
roles: policyRoles, roles: policyRoles,
users: policyUsers, users: policyUsers,
emailWhiteList: policyEmailWhiteList emailWhiteList: policyEmailWhiteList,
rules: policyRules
}; };
} }

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, policyRules, resourcePolicies } from "@server/db"; import { db, resourcePolicyRules, resourcePolicies } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -136,11 +136,13 @@ export async function setResourcePolicyRules(
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId)); .where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId));
await trx await trx
.delete(policyRules) .delete(resourcePolicyRules)
.where(eq(policyRules.resourcePolicyId, resourcePolicyId)); .where(
eq(resourcePolicyRules.resourcePolicyId, resourcePolicyId)
);
if (rules.length > 0) { if (rules.length > 0) {
await trx.insert(policyRules).values( await trx.insert(resourcePolicyRules).values(
rules.map((rule) => ({ rules.map((rule) => ({
resourcePolicyId, resourcePolicyId,
...rule ...rule

View File

@@ -78,7 +78,12 @@ import {
import { ArrowUpDown, Check, ChevronsUpDown, Plus } from "lucide-react"; import { ArrowUpDown, Check, ChevronsUpDown, Plus } from "lucide-react";
import { useCallback, useMemo, useState, useTransition } from "react"; import { useCallback, useMemo, useState, useTransition } from "react";
import { UseFormReturn, useForm } from "react-hook-form"; import { UseFormReturn, useForm, useWatch } from "react-hook-form";
import { useResourcePolicyContext } from "@app/providers/ResourcePolicyProvider";
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import type { AxiosResponse } from "axios";
import { useRouter } from "next/navigation";
// ─── PolicyRulesSection ─────────────────────────────────────────────────────── // ─── PolicyRulesSection ───────────────────────────────────────────────────────
@@ -111,17 +116,31 @@ export function EditPolicyRulesSectionForm({
}: PolicyRulesSectionProps) { }: PolicyRulesSectionProps) {
const t = useTranslations(); const t = useTranslations();
const { policy } = useResourcePolicyContext();
const api = createApiClient(useEnvContext());
const router = useRouter();
const form = useForm({ const form = useForm({
resolver: zodResolver( resolver: zodResolver(
createPolicySchema.pick({ createPolicySchema.pick({
rules: true, rules: true,
applyRules: true applyRules: true
}) })
) ),
defaultValues: {
applyRules: policy.applyRules,
rules: policy.rules
}
}); });
const [isExpanded, setIsExpanded] = useState(false);
const [rules, setRules] = useState<LocalRule[]>([]); const rulesEnabled = useWatch({
const [rulesEnabled, setRulesEnabled] = useState(false); control: form.control,
name: "applyRules"
});
const [rules, setRules] = useState<LocalRule[]>(policy.rules);
const [isExpanded, setIsExpanded] = useState(rulesEnabled);
const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] = const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] =
useState(false); useState(false);
const [openAddRuleAsnSelect, setOpenAddRuleAsnSelect] = useState(false); const [openAddRuleAsnSelect, setOpenAddRuleAsnSelect] = useState(false);
@@ -618,6 +637,45 @@ export function EditPolicyRulesSectionForm({
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
async function saveRules() {
const isValid = form.trigger();
if (!isValid) return;
const payload = form.getValues();
console.log({ payload });
try {
const res = await api
.put<
AxiosResponse<{}>
>(`/resource-policy/${policy.resourcePolicyId}/rules`, payload)
.catch((e) => {
toast({
variant: "destructive",
title: t("policyErrorUpdate"),
description: formatAxiosError(
e,
t("policyErrorUpdateDescription")
)
});
});
if (res && res.status === 200) {
toast({
title: t("success"),
description: t("policyUpdatedSuccess")
});
router.refresh();
}
} catch (e) {
toast({
variant: "destructive",
title: t("policyErrorUpdate"),
description: t("policyErrorUpdateMessageDescription")
});
}
}
if (!isExpanded) { if (!isExpanded) {
return ( return (
<SettingsSection> <SettingsSection>
@@ -659,9 +717,8 @@ export function EditPolicyRulesSectionForm({
<SwitchInput <SwitchInput
id="rules-toggle" id="rules-toggle"
label={t("rulesEnable")} label={t("rulesEnable")}
defaultChecked={false} defaultChecked={rulesEnabled}
onCheckedChange={(val) => { onCheckedChange={(val) => {
setRulesEnabled(val);
form.setValue("applyRules", val); form.setValue("applyRules", val);
}} }}
/> />
@@ -1075,9 +1132,9 @@ export function EditPolicyRulesSectionForm({
</SettingsSectionBody> </SettingsSectionBody>
<SettingsSectionFooter> <SettingsSectionFooter>
<Button <Button
// onClick={saveAllSettings} onClick={() => startTransition(() => saveRules())}
// loading={loading} loading={isPending}
// disabled={loading} disabled={isPending}
> >
{t("rulesSave")} {t("rulesSave")}
</Button> </Button>