Enforece some more things on the types

This commit is contained in:
Owen
2026-06-05 16:57:53 -07:00
parent 13efa47db7
commit d1af7a153f
3 changed files with 137 additions and 65 deletions

View File

@@ -579,6 +579,13 @@ export async function updateProxyResources(
? (resourceData["proxy-protocol-version"] ?? ? (resourceData["proxy-protocol-version"] ??
1) 1)
: 1, : 1,
pamMode:
resourceData["auth-daemon"]?.pam ||
"passthrough",
authDaemonMode:
resourceData["auth-daemon"]?.mode || "native",
authDaemonPort:
resourceData["auth-daemon"]?.port || 22123,
resourcePolicyId: null, resourcePolicyId: null,
defaultResourcePolicyId: inlinePolicyId defaultResourcePolicyId: inlinePolicyId
}) })

View File

@@ -268,8 +268,37 @@ export const PublicResourceSchema = z
return true; return true;
} }
// If protocol/mode is http, it must have a full-domain const effectiveProtocol = resource.mode ?? resource.protocol;
if ((resource.mode ?? resource.protocol) === "http") { if (effectiveProtocol !== "ssh") {
return true;
}
const authDaemonMode = resource["auth-daemon"]?.mode;
if (authDaemonMode !== "native" && authDaemonMode !== "site") {
return true;
}
return (
resource.targets.filter((target) => target != null).length <= 1
);
},
{
path: ["targets"],
error: "When protocol is 'ssh' and auth-daemon mode is 'native' or 'site', only one target/site is allowed"
}
)
.refine(
(resource) => {
if (isTargetsOnlyResource(resource)) {
return true;
}
// If protocol/mode is http, ssh, rdp, or vnc, it must have a full-domain
const effectiveProtocol = resource.mode ?? resource.protocol;
if (
effectiveProtocol !== undefined &&
["http", "ssh", "rdp", "vnc"].includes(effectiveProtocol)
) {
return ( return (
resource["full-domain"] !== undefined && resource["full-domain"] !== undefined &&
resource["full-domain"].length > 0 resource["full-domain"].length > 0
@@ -279,7 +308,7 @@ export const PublicResourceSchema = z
}, },
{ {
path: ["full-domain"], path: ["full-domain"],
error: "When protocol is 'http', a 'full-domain' must be provided" error: "When protocol is 'http', 'ssh', 'rdp', or 'vnc', a 'full-domain' must be provided"
} }
) )
.refine( .refine(
@@ -506,7 +535,44 @@ export const PrivateResourceSchema = z
{ {
message: "Destination must be a valid CIDR notation for cidr mode" message: "Destination must be a valid CIDR notation for cidr mode"
} }
); )
.refine(
(data) => {
if (data.mode !== "ssh") {
return true;
}
const authDaemonMode = data["auth-daemon"]?.mode;
if (authDaemonMode !== "native" && authDaemonMode !== "site") {
return true;
}
const uniqueSites = new Set<string>();
if (data.site) {
uniqueSites.add(data.site);
}
for (const site of data.sites) {
uniqueSites.add(site);
}
return uniqueSites.size <= 1;
},
{
path: ["sites"],
message:
"When mode is 'ssh' and auth-daemon mode is 'native' or 'site', only one site/target is allowed"
}
)
.transform((data) => {
if (
data.mode === "ssh" &&
data.destination !== undefined &&
data["destination-port"] === undefined
) {
data["destination-port"] = 22;
}
return data;
});
export const ResourcePolicyRuleSchema = RuleSchema; export const ResourcePolicyRuleSchema = RuleSchema;

View File

@@ -205,7 +205,8 @@ function SshServerForm({
? [] ? []
: targets.map((target) => ({ : targets.map((target) => ({
targetId: target.targetId, targetId: target.targetId,
siteId: target.siteId siteId: target.siteId,
authToken: target.authToken
})) }))
); );
@@ -253,7 +254,8 @@ function SshServerForm({
}); });
if (isNative) { if (isNative) {
if (values.selectedNativeSite) { const nativeSite = values.selectedNativeSite;
if (nativeSite) {
if (nativeExistingTarget) { if (nativeExistingTarget) {
await api.post( await api.post(
`/target/${nativeExistingTarget.targetId}`, `/target/${nativeExistingTarget.targetId}`,
@@ -261,16 +263,20 @@ function SshServerForm({
mode: "ssh", mode: "ssh",
ip: "localhost", ip: "localhost",
port: 22, port: 22,
siteId: selectedNativeSite.siteId, siteId: nativeSite.siteId,
authToken: nativeExistingTarget.authToken, authToken: nativeExistingTarget.authToken,
hcEnabled: false hcEnabled: false
} }
); );
setNativeExistingTarget({
...nativeExistingTarget,
siteId: nativeSite.siteId
});
} else { } else {
const res = await api.put( const res = await api.put(
`/resource/${resource.resourceId}/target`, `/resource/${resource.resourceId}/target`,
{ {
siteId: selectedNativeSite.siteId, siteId: nativeSite.siteId,
mode: "ssh", mode: "ssh",
ip: "localhost", ip: "localhost",
port: 22, port: 22,
@@ -279,7 +285,7 @@ function SshServerForm({
); );
setNativeExistingTarget({ setNativeExistingTarget({
targetId: res.data.data.targetId, targetId: res.data.data.targetId,
siteId: selectedNativeSite.siteId, siteId: nativeSite.siteId,
authToken: res.data.data.authToken authToken: res.data.data.authToken
}); });
} }
@@ -304,71 +310,64 @@ function SshServerForm({
(t) => !selectedSiteIds.has(t.siteId) (t) => !selectedSiteIds.has(t.siteId)
); );
await Promise.all( await Promise.all(
toDelete.map((t) => toDelete.map((t) => api.delete(`/target/${t.targetId}`))
api.delete( );
`/org/${orgId}/browser-gateway-target/${t.browserGatewayTargetId}`
>>>>>>> 8ee520dbb58f6bd4009581c79322f77b17ff6757 const toUpdate = existingTargets.filter((t) =>
) selectedSiteIds.has(t.siteId)
);
await Promise.all(
toUpdate.map((t) =>
api.post(`/target/${t.targetId}`, {
mode: "ssh",
ip: values.destination,
port: Number(values.destinationPort),
siteId: t.siteId,
authToken: t.authToken,
hcEnabled: false
})
) )
); );
<<<<<<< HEAD const toCreate = activeSites.filter(
const toUpdate = existingTargets.filter((t) => (s) => !existingSiteIds.has(s.siteId)
selectedSiteIds.has(t.siteId)
);
await Promise.all(
toUpdate.map((t) =>
api.post(
`/target/${t.targetId}`,
{
mode: "ssh",
ip: bgDestination,
api.delete(`/target/${t.targetId}`)
}
)
); );
const created = await Promise.all(
<<<<<<< HEAD toCreate.map((s) =>
const toCreate = selectedSites.filter( api.put(`/resource/${resource.resourceId}/target`, {
(s) => !existingSiteIds.has(s.siteId) siteId: s.siteId,
); mode: "ssh",
`/target/${t.targetId}`, ip: values.destination,
toCreate.map((s) => port: Number(values.destinationPort),
mode: "ssh", hcEnabled: false
ip: values.destination, })
port: Number(values.destinationPort),
siteId: t.siteId,
authToken: t.authToken,
hcEnabled: false
port: Number(bgDestinationPort),
hcEnabled: false
)
);
) )
); );
<<<<<<< HEAD
const newTargets: ExistingTarget[] = created.map(
(res, i) => ({
`/resource/${resource.resourceId}/target`,
siteId: toCreate[i].siteId,
authToken: res.data.data.authToken
mode: "ssh",
ip: values.destination,
port: Number(values.destinationPort),
hcEnabled: false
const newTargets: ExistingTarget[] = created.map((res, i) => ({ const newTargets: ExistingTarget[] = created.map((res, i) => ({
browserGatewayTargetId: targetId: res.data.data.targetId,
) siteId: toCreate[i].siteId,
); authToken: res.data.data.authToken
}));
setExistingTargets([...toUpdate, ...newTargets]);
}
toast({
title: t("settingsUpdated"),
description: t("settingsUpdatedDescription")
}); });
const newTargets: ExistingTarget[] = created.map((res, i) => ({ router.refresh();
targetId: res.data.data.targetId, } catch (err) {
siteId: toCreate[i].siteId, console.error(err);
authToken: res.data.data.authToken toast({
})); variant: "destructive",
setExistingTargets([...toUpdate, ...newTargets]); title: t("settingsErrorUpdate"),
description: formatAxiosError(
err,
t("settingsErrorUpdateDescription")
)
});
}
} }
const authMethodOptions: StrategyOption<"passthrough" | "push">[] = [ const authMethodOptions: StrategyOption<"passthrough" | "push">[] = [