This commit is contained in:
Fred KISSIE
2026-03-11 05:18:22 +01:00
parent 304ab1964c
commit 36bcba332c
7 changed files with 90 additions and 42 deletions

View File

@@ -735,6 +735,7 @@
"resourcePolicyOtpEmpty": "No one time password",
"resourcePolicyTypeSave": "Save Resource type",
"resourcePolicySelect": "Select resource policy",
"resourcePolicyNotFound": "Policy not found",
"resourcePolicyRulesEmpty": "No authentication rules",
"resourceAuthMethodsDescriptions": "Allow access to the resource via additional auth methods",
"resourceAuthSettingsSave": "Saved successfully",

View File

@@ -637,10 +637,10 @@ authenticated.get(
);
authenticated.get(
"/resource/:resourceId/default-policy",
"/resource/:resourceId/policies",
verifyResourceAccess,
verifyUserHasAction(ActionsEnum.getResourcePolicy),
resource.getDefaultResourcePolicy
resource.getResourcePolicies
);
authenticated.put(

View File

@@ -454,10 +454,10 @@ authenticated.get(
);
authenticated.get(
"/resource/:resourceId/default-policy",
"/resource/:resourceId/policies",
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.getResourcePolicy),
resource.getDefaultResourcePolicy
resource.getResourcePolicies
);
authenticated.post(

View File

@@ -17,12 +17,15 @@ const getResourcePoliciesParamsSchema = z.strictObject({
resourceId: z.string().transform(Number).pipe(z.int().positive())
});
export type GetDefaultResourcePolicyResponse = GetResourcePolicyResponse;
export type GetResourcePoliciesResponse = {
defaultPolicy: GetResourcePolicyResponse;
sharedPolicy: GetResourcePolicyResponse | null;
};
registry.registerPath({
method: "get",
path: "/resource/{resourceId}/default-policy",
description: "Get the default policy for a resource.",
path: "/resource/{resourceId}/policies",
description: "Get the inline and shared policies associated with a resource.",
tags: [OpenAPITags.PublicResource, OpenAPITags.Policy],
request: {
params: getResourcePoliciesParamsSchema
@@ -30,7 +33,7 @@ registry.registerPath({
responses: {}
});
export async function getDefaultResourcePolicy(
export async function getResourcePolicies(
req: Request,
res: Response,
next: NextFunction
@@ -52,7 +55,8 @@ export async function getDefaultResourcePolicy(
const [resource] = await db
.select({
defaultResourcePolicyId: resources.defaultResourcePolicyId
defaultResourcePolicyId: resources.defaultResourcePolicyId,
resourcePolicyId: resources.resourcePolicyId
})
.from(resources)
.where(eq(resources.resourceId, resourceId))
@@ -73,11 +77,24 @@ export async function getDefaultResourcePolicy(
);
}
const defaultPolicy = await queryResourcePolicy({
resourcePolicyId: resource.defaultResourcePolicyId
});
return response<GetDefaultResourcePolicyResponse>(res, {
data: defaultPolicy,
const [defaultPolicy, sharedPolicy] = await Promise.all([
queryResourcePolicy({
resourcePolicyId: resource.defaultResourcePolicyId
}),
resource.resourcePolicyId
? queryResourcePolicy({
resourcePolicyId: resource.resourcePolicyId
})
: null
]);
return response<GetResourcePoliciesResponse>(res, {
data: {
defaultPolicy:
// the policy will always be non nullable
defaultPolicy as unknown as GetResourcePolicyResponse,
sharedPolicy
},
success: true,
error: false,
message: "Resource policies retrieved successfully",

View File

@@ -31,4 +31,4 @@ export * from "./addUserToResource";
export * from "./removeUserFromResource";
export * from "./listAllResourceNames";
export * from "./removeEmailFromResourceWhitelist";
export * from "./getDefaultResourcePolicy";
export * from "./getResourcePolicies";

View File

@@ -79,7 +79,7 @@ import SetResourcePasswordForm from "components/SetResourcePasswordForm";
import { Binary, Bot, CheckIcon, InfoIcon, Key } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import {
import React, {
useActionState,
useEffect,
useMemo,
@@ -105,8 +105,7 @@ type ResourcePolicyType = StrategyOption<"inline" | "shared">;
export default function ResourceAuthenticationPage() {
const { org } = useOrgContext();
const { resource, updateResource, authInfo, updateAuthInfo } =
useResourceContext();
const { resource, updateResource } = useResourceContext();
const { env } = useEnvContext();
@@ -125,7 +124,7 @@ export default function ResourceAuthenticationPage() {
const form = useForm({
resolver: zodResolver(resourceTypeSchema),
defaultValues: {
type: "inline"
type: resource.resourcePolicyId ? "shared" : "inline"
}
});
@@ -145,7 +144,7 @@ export default function ResourceAuthenticationPage() {
enabled: selectedResourceType === "shared"
});
const { data: sharedPolicy } = useQuery({
const { data: sharedPolicy, isLoading: isLoadingSharedPolicy } = useQuery({
...resourcePolicyQueries.single({
resourcePolicyId: resource.resourcePolicyId ?? 1
}),
@@ -157,17 +156,6 @@ export default function ResourceAuthenticationPage() {
id: number;
} | null>(null);
const pageLoading = isLoadingPolicies || !defaultPolicy;
const [
loadingRemoveResourceHeaderAuth,
setLoadingRemoveResourceHeaderAuth
] = useState(false);
const [isSetPasswordOpen, setIsSetPasswordOpen] = useState(false);
const [isSetPincodeOpen, setIsSetPincodeOpen] = useState(false);
const [isSetHeaderAuthOpen, setIsSetHeaderAuthOpen] = useState(false);
const resourcePolicyTypes: Array<ResourcePolicyType> = [
{
id: "inline",
@@ -181,6 +169,49 @@ export default function ResourceAuthenticationPage() {
}
];
useEffect(() => {
if (!isLoadingSharedPolicy && sharedPolicy) {
setSelectedPolicy({
id: sharedPolicy.resourcePolicyId,
name: sharedPolicy.name
});
}
}, [isLoadingSharedPolicy, sharedPolicy]);
const [isUpdatingResource, startTransition] = useTransition();
async function handleSaveResourcePolicyType() {
try {
if (selectedResourceType === "inline") {
await api.post(`/resource/${resource.resourceId}`, {
resourcePolicyId: null
});
} else {
if (!selectedPolicy) {
toast({
title: t("error"),
description: t("resourcePolicySelectError"),
variant: "destructive"
});
return;
}
await api.post(`/resource/${resource.resourceId}`, {
resourcePolicyId: selectedPolicy.id
});
}
router.refresh();
} catch (e) {
toast({
title: t("error"),
description: formatAxiosError(e),
variant: "destructive"
});
}
}
const pageLoading =
isLoadingPolicies || !defaultPolicy || isLoadingSharedPolicy;
if (pageLoading) {
return <></>;
}
@@ -214,9 +245,6 @@ export default function ResourceAuthenticationPage() {
role="combobox"
className={
"w-full md:w-1/2 justify-between"
// "w-45 justify-between text-sm border-r pr-4 rounded-none h-8 hover:bg-transparent",
// "rounded-l-md rounded-r-xs"
// !proxyTarget.siteId && "text-muted-foreground"
}
>
<span className="truncate max-w-37.5">
@@ -238,7 +266,7 @@ export default function ResourceAuthenticationPage() {
/>
<CommandList>
<CommandEmpty>
{t("siteNotFound")}
{t("resourcePolicyNotFound")}
</CommandEmpty>
<CommandGroup>
{policiesList.map((policy) => (
@@ -275,9 +303,10 @@ export default function ResourceAuthenticationPage() {
</SettingsSectionBody>
<SettingsSectionFooter className="justify-start">
<Button
onClick={() => {
//...
}}
onClick={() =>
startTransition(handleSaveResourcePolicyType)
}
loading={isUpdatingResource}
>
{t("resourcePolicyTypeSave")}
</Button>

View File

@@ -4,7 +4,7 @@ import type { ListClientsResponse } from "@server/routers/client";
import type { ListDomainsResponse } from "@server/routers/domain";
import type {
GetResourceWhitelistResponse,
GetDefaultResourcePolicyResponse,
GetResourcePoliciesResponse,
ListResourceNamesResponse,
ListResourcesResponse,
ListResourceRolesResponse,
@@ -31,6 +31,7 @@ import { remote } from "./api";
import { durationToMs } from "./durationToMs";
import { wait } from "./wait";
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
import type { GetResourcePolicyResponse } from "@server/routers/policy";
export type ProductUpdate = {
link: string | null;
@@ -227,7 +228,7 @@ export const resourcePolicyQueries = {
queryKey: ["RESOURCE_POLICIES", resourcePolicyId] as const,
queryFn: async ({ signal, meta }) => {
const res = await meta!.api.get<
AxiosResponse<GetDefaultResourcePolicyResponse>
AxiosResponse<GetResourcePolicyResponse>
>(`/resource-policy/${resourcePolicyId}`, { signal });
return res.data.data;
@@ -364,8 +365,8 @@ export const resourceQueries = {
queryKey: ["RESOURCES", resourceId, "DEFAULT_POLICY"] as const,
queryFn: async ({ signal, meta }) => {
const res = await meta!.api.get<
AxiosResponse<GetDefaultResourcePolicyResponse>
>(`/resource/${resourceId}/default-policy`, { signal });
AxiosResponse<GetResourcePoliciesResponse>
>(`/resource/${resourceId}/policies`, { signal });
return res.data.data;
}