mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-10 20:02:26 +00:00
add optial disconnect on regenerate credentials
This commit is contained in:
@@ -2221,7 +2221,7 @@
|
|||||||
"credentials": "Credentials",
|
"credentials": "Credentials",
|
||||||
"savecredentials": "Save Credentials",
|
"savecredentials": "Save Credentials",
|
||||||
"regenerateCredentialsButton": "Regenerate Credentials",
|
"regenerateCredentialsButton": "Regenerate Credentials",
|
||||||
"regenerateCredentials": "Regenerate and save your credentials",
|
"regenerateCredentials": "Regenerate Credentials",
|
||||||
"generatedcredentials": "Generated Credentials",
|
"generatedcredentials": "Generated Credentials",
|
||||||
"copyandsavethesecredentials": "Copy and save these credentials",
|
"copyandsavethesecredentials": "Copy and save these credentials",
|
||||||
"copyandsavethesecredentialsdescription": "These credentials will not be shown again after you leave this page. Save them securely now.",
|
"copyandsavethesecredentialsdescription": "These credentials will not be shown again after you leave this page. Save them securely now.",
|
||||||
@@ -2253,5 +2253,20 @@
|
|||||||
"clientAddress": "Client Address (Advanced)",
|
"clientAddress": "Client Address (Advanced)",
|
||||||
"setupFailedToFetchSubnet": "Failed to fetch default subnet",
|
"setupFailedToFetchSubnet": "Failed to fetch default subnet",
|
||||||
"setupSubnetAdvanced": "Subnet (Advanced)",
|
"setupSubnetAdvanced": "Subnet (Advanced)",
|
||||||
"setupSubnetDescription": "The subnet for this organization's internal network."
|
"setupSubnetDescription": "The subnet for this organization's internal network.",
|
||||||
|
"siteRegenerateAndDisconnect": "Regenerate and Disconnect",
|
||||||
|
"siteRegenerateAndDisconnectConfirmation": "Are you sure you want to regenerate the credentials and disconnect this site?",
|
||||||
|
"siteRegenerateAndDisconnectWarning": "This will regenerate the credentials and immediately disconnect the site. The site will need to be restarted with the new credentials.",
|
||||||
|
"siteRegenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials for this site?",
|
||||||
|
"siteRegenerateCredentialsWarning": "This will regenerate the credentials. The site will stay connected until you manually restart it and use the new credentials.",
|
||||||
|
"clientRegenerateAndDisconnect": "Regenerate and Disconnect",
|
||||||
|
"clientRegenerateAndDisconnectConfirmation": "Are you sure you want to regenerate the credentials and disconnect this client?",
|
||||||
|
"clientRegenerateAndDisconnectWarning": "This will regenerate the credentials and immediately disconnect the client. The client will need to be restarted with the new credentials.",
|
||||||
|
"clientRegenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials for this client?",
|
||||||
|
"clientRegenerateCredentialsWarning": "This will regenerate the credentials. The client will stay connected until you manually restart it and use the new credentials.",
|
||||||
|
"remoteExitNodeRegenerateAndDisconnect": "Regenerate and Disconnect",
|
||||||
|
"remoteExitNodeRegenerateAndDisconnectConfirmation": "Are you sure you want to regenerate the credentials and disconnect this remote exit node?",
|
||||||
|
"remoteExitNodeRegenerateAndDisconnectWarning": "This will regenerate the credentials and immediately disconnect the remote exit node. The remote exit node will need to be restarted with the new credentials.",
|
||||||
|
"remoteExitNodeRegenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials for this remote exit node?",
|
||||||
|
"remoteExitNodeRegenerateCredentialsWarning": "This will regenerate the credentials. The remote exit node will stay connected until you manually restart it and use the new credentials."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ const reGenerateSecretParamsSchema = z.strictObject({
|
|||||||
|
|
||||||
const reGenerateSecretBodySchema = z.strictObject({
|
const reGenerateSecretBodySchema = z.strictObject({
|
||||||
// olmId: z.string().min(1).optional(),
|
// olmId: z.string().min(1).optional(),
|
||||||
secret: z.string().min(1)
|
secret: z.string().min(1),
|
||||||
|
disconnect: z.boolean().optional().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;
|
export type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;
|
||||||
@@ -52,7 +53,7 @@ export async function reGenerateClientSecret(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { secret } = parsedBody.data;
|
const { secret, disconnect } = parsedBody.data;
|
||||||
|
|
||||||
const parsedParams = reGenerateSecretParamsSchema.safeParse(req.params);
|
const parsedParams = reGenerateSecretParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
@@ -114,18 +115,21 @@ export async function reGenerateClientSecret(
|
|||||||
})
|
})
|
||||||
.where(eq(olms.olmId, existingOlms[0].olmId));
|
.where(eq(olms.olmId, existingOlms[0].olmId));
|
||||||
|
|
||||||
const payload = {
|
// Only disconnect if explicitly requested
|
||||||
type: `olm/terminate`,
|
if (disconnect) {
|
||||||
data: {}
|
const payload = {
|
||||||
};
|
type: `olm/terminate`,
|
||||||
// Don't await this to prevent blocking the response
|
data: {}
|
||||||
sendToClient(existingOlms[0].olmId, payload).catch((error) => {
|
};
|
||||||
logger.error("Failed to send termination message to olm:", error);
|
// Don't await this to prevent blocking the response
|
||||||
});
|
sendToClient(existingOlms[0].olmId, payload).catch((error) => {
|
||||||
|
logger.error("Failed to send termination message to olm:", error);
|
||||||
|
});
|
||||||
|
|
||||||
disconnectClient(existingOlms[0].olmId).catch((error) => {
|
disconnectClient(existingOlms[0].olmId).catch((error) => {
|
||||||
logger.error("Failed to disconnect olm after re-key:", error);
|
logger.error("Failed to disconnect olm after re-key:", error);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { hashPassword } from "@server/auth/password";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { disconnectClient } from "#private/routers/ws";
|
import { disconnectClient, sendToClient } from "#private/routers/ws";
|
||||||
|
|
||||||
export const paramsSchema = z.object({
|
export const paramsSchema = z.object({
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
@@ -31,7 +31,8 @@ export const paramsSchema = z.object({
|
|||||||
|
|
||||||
const bodySchema = z.strictObject({
|
const bodySchema = z.strictObject({
|
||||||
remoteExitNodeId: z.string().length(15),
|
remoteExitNodeId: z.string().length(15),
|
||||||
secret: z.string().length(48)
|
secret: z.string().length(48),
|
||||||
|
disconnect: z.boolean().optional().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function reGenerateExitNodeSecret(
|
export async function reGenerateExitNodeSecret(
|
||||||
@@ -60,7 +61,7 @@ export async function reGenerateExitNodeSecret(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { remoteExitNodeId, secret } = parsedBody.data;
|
const { remoteExitNodeId, secret, disconnect } = parsedBody.data;
|
||||||
|
|
||||||
const [existingRemoteExitNode] = await db
|
const [existingRemoteExitNode] = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -83,11 +84,31 @@ export async function reGenerateExitNodeSecret(
|
|||||||
.set({ secretHash })
|
.set({ secretHash })
|
||||||
.where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId));
|
.where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId));
|
||||||
|
|
||||||
disconnectClient(existingRemoteExitNode.remoteExitNodeId).catch(
|
// Only disconnect if explicitly requested
|
||||||
(error) => {
|
if (disconnect) {
|
||||||
logger.error("Failed to disconnect newt after re-key:", error);
|
const payload = {
|
||||||
}
|
type: `remoteExitNode/terminate`,
|
||||||
);
|
data: {}
|
||||||
|
};
|
||||||
|
// Don't await this to prevent blocking the response
|
||||||
|
sendToClient(existingRemoteExitNode.remoteExitNodeId, payload).catch(
|
||||||
|
(error) => {
|
||||||
|
logger.error(
|
||||||
|
"Failed to send termination message to remote exit node:",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
disconnectClient(existingRemoteExitNode.remoteExitNodeId).catch(
|
||||||
|
(error) => {
|
||||||
|
logger.error(
|
||||||
|
"Failed to disconnect remote exit node after re-key:",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: null,
|
data: null,
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ const updateSiteParamsSchema = z.strictObject({
|
|||||||
const updateSiteBodySchema = z.strictObject({
|
const updateSiteBodySchema = z.strictObject({
|
||||||
type: z.enum(["newt", "wireguard"]),
|
type: z.enum(["newt", "wireguard"]),
|
||||||
secret: z.string().min(1).max(255).optional(),
|
secret: z.string().min(1).max(255).optional(),
|
||||||
pubKey: z.string().optional()
|
pubKey: z.string().optional(),
|
||||||
|
disconnect: z.boolean().optional().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function reGenerateSiteSecret(
|
export async function reGenerateSiteSecret(
|
||||||
@@ -63,7 +64,7 @@ export async function reGenerateSiteSecret(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { siteId } = parsedParams.data;
|
const { siteId } = parsedParams.data;
|
||||||
const { type, pubKey, secret } = parsedBody.data;
|
const { type, pubKey, secret, disconnect } = parsedBody.data;
|
||||||
|
|
||||||
let existingNewt: Newt | null = null;
|
let existingNewt: Newt | null = null;
|
||||||
if (type === "newt") {
|
if (type === "newt") {
|
||||||
@@ -112,21 +113,24 @@ export async function reGenerateSiteSecret(
|
|||||||
})
|
})
|
||||||
.where(eq(newts.newtId, existingNewts[0].newtId));
|
.where(eq(newts.newtId, existingNewts[0].newtId));
|
||||||
|
|
||||||
const payload = {
|
// Only disconnect if explicitly requested
|
||||||
type: `newt/wg/terminate`,
|
if (disconnect) {
|
||||||
data: {}
|
const payload = {
|
||||||
};
|
type: `newt/wg/terminate`,
|
||||||
// Don't await this to prevent blocking the response
|
data: {}
|
||||||
sendToClient(existingNewts[0].newtId, payload).catch((error) => {
|
};
|
||||||
logger.error(
|
// Don't await this to prevent blocking the response
|
||||||
"Failed to send termination message to newt:",
|
sendToClient(existingNewts[0].newtId, payload).catch((error) => {
|
||||||
error
|
logger.error(
|
||||||
);
|
"Failed to send termination message to newt:",
|
||||||
});
|
error
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
disconnectClient(existingNewts[0].newtId).catch((error) => {
|
disconnectClient(existingNewts[0].newtId).catch((error) => {
|
||||||
logger.error("Failed to disconnect newt after re-key:", error);
|
logger.error("Failed to disconnect newt after re-key:", error);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(`Regenerated Newt credentials for site ${siteId}`);
|
logger.info(`Regenerated Newt credentials for site ${siteId}`);
|
||||||
} else if (type === "wireguard") {
|
} else if (type === "wireguard") {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default function CredentialsPage() {
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
||||||
|
const [shouldDisconnect, setShouldDisconnect] = useState(true);
|
||||||
|
|
||||||
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
||||||
const subscription = useSubscriptionStatusContext();
|
const subscription = useSubscriptionStatusContext();
|
||||||
@@ -79,7 +80,8 @@ export default function CredentialsPage() {
|
|||||||
AxiosResponse<QuickStartRemoteExitNodeResponse>
|
AxiosResponse<QuickStartRemoteExitNodeResponse>
|
||||||
>(`/re-key/${orgId}/regenerate-remote-exit-node-secret`, {
|
>(`/re-key/${orgId}/regenerate-remote-exit-node-secret`, {
|
||||||
remoteExitNodeId: remoteExitNode.remoteExitNodeId,
|
remoteExitNodeId: remoteExitNode.remoteExitNodeId,
|
||||||
secret: data.secret
|
secret: data.secret,
|
||||||
|
disconnect: shouldDisconnect
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rekeyRes && rekeyRes.status === 200) {
|
if (rekeyRes && rekeyRes.status === 200) {
|
||||||
@@ -193,12 +195,27 @@ export default function CredentialsPage() {
|
|||||||
)}
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
<SettingsSectionFooter>
|
<SettingsSectionFooter>
|
||||||
<Button
|
<div className="flex gap-2">
|
||||||
onClick={() => setModalOpen(true)}
|
<Button
|
||||||
disabled={isSecurityFeatureDisabled()}
|
variant="outline"
|
||||||
>
|
onClick={() => {
|
||||||
{t("regenerateCredentialsButton")}
|
setShouldDisconnect(false);
|
||||||
</Button>
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("regenerateCredentialsButton")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setShouldDisconnect(true);
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("remoteExitNodeRegenerateAndDisconnect")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</SettingsSectionFooter>
|
</SettingsSectionFooter>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
@@ -216,11 +233,32 @@ export default function CredentialsPage() {
|
|||||||
}}
|
}}
|
||||||
dialog={
|
dialog={
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>{t("regenerateCredentialsConfirmation")}</p>
|
{shouldDisconnect ? (
|
||||||
<p>{t("regenerateCredentialsWarning")}</p>
|
<>
|
||||||
|
<p>
|
||||||
|
{t("remoteExitNodeRegenerateAndDisconnectConfirmation")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("remoteExitNodeRegenerateAndDisconnectWarning")}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t("remoteExitNodeRegenerateCredentialsConfirmation")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("remoteExitNodeRegenerateCredentialsWarning")}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
buttonText={t("regenerateCredentialsButton")}
|
buttonText={
|
||||||
|
shouldDisconnect
|
||||||
|
? t("remoteExitNodeRegenerateAndDisconnect")
|
||||||
|
: t("regenerateCredentialsButton")
|
||||||
|
}
|
||||||
onConfirm={handleConfirmRegenerate}
|
onConfirm={handleConfirmRegenerate}
|
||||||
string={getConfirmationString()}
|
string={getConfirmationString()}
|
||||||
title={t("regenerateCredentials")}
|
title={t("regenerateCredentials")}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export default function CredentialsPage() {
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
||||||
|
const [shouldDisconnect, setShouldDisconnect] = useState(true);
|
||||||
|
|
||||||
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
||||||
const subscription = useSubscriptionStatusContext();
|
const subscription = useSubscriptionStatusContext();
|
||||||
@@ -69,7 +70,8 @@ export default function CredentialsPage() {
|
|||||||
const rekeyRes = await api.post(
|
const rekeyRes = await api.post(
|
||||||
`/re-key/${client?.clientId}/regenerate-client-secret`,
|
`/re-key/${client?.clientId}/regenerate-client-secret`,
|
||||||
{
|
{
|
||||||
secret: data.olmSecret
|
secret: data.olmSecret,
|
||||||
|
disconnect: shouldDisconnect
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -173,12 +175,27 @@ export default function CredentialsPage() {
|
|||||||
)}
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
<SettingsSectionFooter>
|
<SettingsSectionFooter>
|
||||||
<Button
|
<div className="flex gap-2">
|
||||||
onClick={() => setModalOpen(true)}
|
<Button
|
||||||
disabled={isSecurityFeatureDisabled()}
|
variant="outline"
|
||||||
>
|
onClick={() => {
|
||||||
{t("regenerateCredentialsButton")}
|
setShouldDisconnect(false);
|
||||||
</Button>
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("regenerateCredentialsButton")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setShouldDisconnect(true);
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("clientRegenerateAndDisconnect")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</SettingsSectionFooter>
|
</SettingsSectionFooter>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
@@ -196,11 +213,32 @@ export default function CredentialsPage() {
|
|||||||
}}
|
}}
|
||||||
dialog={
|
dialog={
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>{t("regenerateCredentialsConfirmation")}</p>
|
{shouldDisconnect ? (
|
||||||
<p>{t("regenerateCredentialsWarning")}</p>
|
<>
|
||||||
|
<p>
|
||||||
|
{t("clientRegenerateAndDisconnectConfirmation")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("clientRegenerateAndDisconnectWarning")}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t("clientRegenerateCredentialsConfirmation")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("clientRegenerateCredentialsWarning")}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
buttonText={t("regenerateCredentialsButton")}
|
buttonText={
|
||||||
|
shouldDisconnect
|
||||||
|
? t("clientRegenerateAndDisconnect")
|
||||||
|
: t("regenerateCredentialsButton")
|
||||||
|
}
|
||||||
onConfirm={handleConfirmRegenerate}
|
onConfirm={handleConfirmRegenerate}
|
||||||
string={getConfirmationString()}
|
string={getConfirmationString()}
|
||||||
title={t("regenerateCredentials")}
|
title={t("regenerateCredentials")}
|
||||||
|
|||||||
@@ -53,13 +53,16 @@ export default function CredentialsPage() {
|
|||||||
useState<PickSiteDefaultsResponse | null>(null);
|
useState<PickSiteDefaultsResponse | null>(null);
|
||||||
const [wgConfig, setWgConfig] = useState("");
|
const [wgConfig, setWgConfig] = useState("");
|
||||||
const [publicKey, setPublicKey] = useState("");
|
const [publicKey, setPublicKey] = useState("");
|
||||||
const [currentNewtId, setCurrentNewtId] = useState<string | null>(site.newtId);
|
const [currentNewtId, setCurrentNewtId] = useState<string | null>(
|
||||||
|
site.newtId
|
||||||
|
);
|
||||||
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
|
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
|
||||||
const [showWireGuardAlert, setShowWireGuardAlert] = useState(false);
|
const [showWireGuardAlert, setShowWireGuardAlert] = useState(false);
|
||||||
const [loadingDefaults, setLoadingDefaults] = useState(false);
|
const [loadingDefaults, setLoadingDefaults] = useState(false);
|
||||||
|
const [shouldDisconnect, setShouldDisconnect] = useState(true);
|
||||||
|
|
||||||
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
const { licenseStatus, isUnlocked } = useLicenseStatusContext();
|
||||||
const subscription = useSubscriptionStatusContext();
|
const subscription = useSubscriptionStatusContext();
|
||||||
@@ -77,7 +80,9 @@ export default function CredentialsPage() {
|
|||||||
if (site?.type === "wireguard" && !siteDefaults && orgId) {
|
if (site?.type === "wireguard" && !siteDefaults && orgId) {
|
||||||
setLoadingDefaults(true);
|
setLoadingDefaults(true);
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`/org/${orgId}/pick-site-defaults`);
|
const res = await api.get(
|
||||||
|
`/org/${orgId}/pick-site-defaults`
|
||||||
|
);
|
||||||
if (res && res.status === 200) {
|
if (res && res.status === 200) {
|
||||||
setSiteDefaults(res.data.data);
|
setSiteDefaults(res.data.data);
|
||||||
}
|
}
|
||||||
@@ -93,7 +98,6 @@ export default function CredentialsPage() {
|
|||||||
fetchSiteDefaults();
|
fetchSiteDefaults();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const handleConfirmRegenerate = async () => {
|
const handleConfirmRegenerate = async () => {
|
||||||
try {
|
try {
|
||||||
let generatedPublicKey = "";
|
let generatedPublicKey = "";
|
||||||
@@ -140,7 +144,8 @@ export default function CredentialsPage() {
|
|||||||
`/re-key/${site?.siteId}/regenerate-site-secret`,
|
`/re-key/${site?.siteId}/regenerate-site-secret`,
|
||||||
{
|
{
|
||||||
type: "newt",
|
type: "newt",
|
||||||
secret: data.newtSecret
|
secret: data.newtSecret,
|
||||||
|
disconnect: shouldDisconnect
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -233,7 +238,11 @@ export default function CredentialsPage() {
|
|||||||
text={displaySecret}
|
text={displaySecret}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span>{"••••••••••••••••••••••••••••••••"}</span>
|
<span>
|
||||||
|
{
|
||||||
|
"••••••••••••••••••••••••••••••••"
|
||||||
|
}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
@@ -252,12 +261,27 @@ export default function CredentialsPage() {
|
|||||||
)}
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
<SettingsSectionFooter>
|
<SettingsSectionFooter>
|
||||||
<Button
|
<div className="flex gap-2">
|
||||||
onClick={() => setModalOpen(true)}
|
<Button
|
||||||
disabled={isSecurityFeatureDisabled()}
|
variant="outline"
|
||||||
>
|
onClick={() => {
|
||||||
{t("regenerateCredentialsButton")}
|
setShouldDisconnect(false);
|
||||||
</Button>
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("regenerateCredentialsButton")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setShouldDisconnect(true);
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
disabled={isSecurityFeatureDisabled()}
|
||||||
|
>
|
||||||
|
{t("siteRegenerateAndDisconnect")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</SettingsSectionFooter>
|
</SettingsSectionFooter>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
)}
|
)}
|
||||||
@@ -280,7 +304,10 @@ export default function CredentialsPage() {
|
|||||||
<>
|
<>
|
||||||
{wgConfig ? (
|
{wgConfig ? (
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<CopyTextBox text={wgConfig} outline={true} />
|
<CopyTextBox
|
||||||
|
text={wgConfig}
|
||||||
|
outline={true}
|
||||||
|
/>
|
||||||
<div className="relative w-fit border rounded-md">
|
<div className="relative w-fit border rounded-md">
|
||||||
<div className="bg-white p-6 rounded-md">
|
<div className="bg-white p-6 rounded-md">
|
||||||
<QRCodeCanvas
|
<QRCodeCanvas
|
||||||
@@ -293,24 +320,47 @@ export default function CredentialsPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<CopyTextBox
|
<CopyTextBox
|
||||||
text={generateObfuscatedWireGuardConfig({
|
text={generateObfuscatedWireGuardConfig(
|
||||||
subnet: siteDefaults?.subnet || site?.subnet || null,
|
{
|
||||||
address: siteDefaults?.address || site?.address || null,
|
subnet:
|
||||||
endpoint: siteDefaults?.endpoint || site?.endpoint || null,
|
siteDefaults?.subnet ||
|
||||||
listenPort: siteDefaults?.listenPort || site?.listenPort || null,
|
site?.subnet ||
|
||||||
publicKey: siteDefaults?.publicKey || site?.publicKey || site?.pubKey || null
|
null,
|
||||||
})}
|
address:
|
||||||
|
siteDefaults?.address ||
|
||||||
|
site?.address ||
|
||||||
|
null,
|
||||||
|
endpoint:
|
||||||
|
siteDefaults?.endpoint ||
|
||||||
|
site?.endpoint ||
|
||||||
|
null,
|
||||||
|
listenPort:
|
||||||
|
siteDefaults?.listenPort ||
|
||||||
|
site?.listenPort ||
|
||||||
|
null,
|
||||||
|
publicKey:
|
||||||
|
siteDefaults?.publicKey ||
|
||||||
|
site?.publicKey ||
|
||||||
|
site?.pubKey ||
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)}
|
||||||
outline={true}
|
outline={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showWireGuardAlert && wgConfig && (
|
{showWireGuardAlert && wgConfig && (
|
||||||
<Alert variant="neutral" className="mt-4">
|
<Alert
|
||||||
|
variant="neutral"
|
||||||
|
className="mt-4"
|
||||||
|
>
|
||||||
<InfoIcon className="h-4 w-4" />
|
<InfoIcon className="h-4 w-4" />
|
||||||
<AlertTitle className="font-semibold">
|
<AlertTitle className="font-semibold">
|
||||||
{t("siteCredentialsSave")}
|
{t("siteCredentialsSave")}
|
||||||
</AlertTitle>
|
</AlertTitle>
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
{t("siteCredentialsSaveDescription")}
|
{t(
|
||||||
|
"siteCredentialsSaveDescription"
|
||||||
|
)}
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
@@ -322,7 +372,7 @@ export default function CredentialsPage() {
|
|||||||
onClick={() => setModalOpen(true)}
|
onClick={() => setModalOpen(true)}
|
||||||
disabled={isSecurityFeatureDisabled()}
|
disabled={isSecurityFeatureDisabled()}
|
||||||
>
|
>
|
||||||
{t("regenerateCredentialsButton")}
|
{t("siteRegenerateAndDisconnect")}
|
||||||
</Button>
|
</Button>
|
||||||
</SettingsSectionFooter>
|
</SettingsSectionFooter>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
@@ -343,11 +393,38 @@ export default function CredentialsPage() {
|
|||||||
}}
|
}}
|
||||||
dialog={
|
dialog={
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>{t("regenerateCredentialsConfirmation")}</p>
|
{shouldDisconnect ? (
|
||||||
<p>{t("regenerateCredentialsWarning")}</p>
|
<>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
"siteRegenerateAndDisconnectConfirmation"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
"siteRegenerateAndDisconnectWarning"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
"siteRegenerateCredentialsConfirmation"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t("siteRegenerateCredentialsWarning")}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
buttonText={t("regenerateCredentialsButton")}
|
buttonText={
|
||||||
|
shouldDisconnect
|
||||||
|
? t("siteRegenerateAndDisconnect")
|
||||||
|
: t("regenerateCredentialsButton")
|
||||||
|
}
|
||||||
onConfirm={handleConfirmRegenerate}
|
onConfirm={handleConfirmRegenerate}
|
||||||
string={getConfirmationString()}
|
string={getConfirmationString()}
|
||||||
title={t("regenerateCredentials")}
|
title={t("regenerateCredentials")}
|
||||||
|
|||||||
Reference in New Issue
Block a user