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"] ??
1)
: 1,
pamMode:
resourceData["auth-daemon"]?.pam ||
"passthrough",
authDaemonMode:
resourceData["auth-daemon"]?.mode || "native",
authDaemonPort:
resourceData["auth-daemon"]?.port || 22123,
resourcePolicyId: null,
defaultResourcePolicyId: inlinePolicyId
})

View File

@@ -268,8 +268,37 @@ export const PublicResourceSchema = z
return true;
}
// If protocol/mode is http, it must have a full-domain
if ((resource.mode ?? resource.protocol) === "http") {
const effectiveProtocol = resource.mode ?? resource.protocol;
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 (
resource["full-domain"] !== undefined &&
resource["full-domain"].length > 0
@@ -279,7 +308,7 @@ export const PublicResourceSchema = z
},
{
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(
@@ -506,7 +535,44 @@ export const PrivateResourceSchema = z
{
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;

View File

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