Skip to content

Commit 409f2f4

Browse files
committed
perf: account login
1 parent 34ea074 commit 409f2f4

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

‎ui/composables/useApiRequest.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,14 @@ export function getAccountDetail(accountId: string, orgId?: string): Promise<Per
723723
});
724724
}
725725

726+
export function getPersonalAssetCredential(credentialId: string, orgId?: string): Promise<PersonalAssetCredential> {
727+
return apiRequest({
728+
method: "GET",
729+
path: `/api/v1/accounts/personal-asset-credentials/${encodeURIComponent(credentialId)}/`,
730+
orgId
731+
});
732+
}
733+
726734
export async function getPersonalAssetCredentials(
727735
assetId: string,
728736
protocol: string,

‎ui/composables/useSessionWindowConnect.ts‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { AssetDetail, AssetItem, PermedAccount, PermOrgItem, RdpGraphics } from "~/types";
2-
import { getAccountDetail, getAssetDetailRequest, getConsoleAssetDetail } from "~/composables/useApiRequest";
2+
import {
3+
getAccountDetail,
4+
getAssetDetailRequest,
5+
getConsoleAssetDetail,
6+
getPersonalAssetCredential
7+
} from "~/composables/useApiRequest";
38
import { desktopInvoke } from "~/shared/desktop/bridge";
49
import { useUserInfoStore } from "~/store/modules/userInfo";
510
import { transformAssetDetail } from "~/utils";
@@ -11,6 +16,7 @@ export interface SessionWindowConnectionInfo {
1116
account: string;
1217
manualUsername: string;
1318
manualPassword: string;
19+
personalCredentialId?: string;
1420
dynamicPassword: string;
1521
rememberSecret: boolean;
1622
rememberSelection?: boolean;
@@ -122,6 +128,9 @@ export function buildSessionPath(asset: AssetItem, connectionInfo?: SessionWindo
122128
query.set("accountMode", connectionInfo.accountMode);
123129
if (connectionInfo.accountId) query.set("accountId", connectionInfo.accountId);
124130
if (connectionInfo.connectMethod) query.set("method", connectionInfo.connectMethod);
131+
if (connectionInfo.accountMode === "manual" && connectionInfo.personalCredentialId) {
132+
query.set("personalCredentialId", connectionInfo.personalCredentialId);
133+
}
125134

126135
return `/session/${encodeURIComponent(asset.id)}?${query.toString()}`;
127136
}
@@ -162,6 +171,7 @@ const sessionAccountModes = new Set<SessionWindowConnectionInfo["accountMode"]>(
162171

163172
export function useSessionWindowConnect() {
164173
const route = useRoute();
174+
const { t } = useI18n();
165175
const { activeTab, openSession, openSetupSession } = useWorkspaceTabs();
166176
const { confirmConnection } = useAssetConnection();
167177
const userInfoStore = useUserInfoStore();
@@ -214,6 +224,51 @@ export function useSessionWindowConnect() {
214224
asset.savedConnection = saved || undefined;
215225
assetName.value = asset.name || "JumpServer";
216226

227+
// An explicit credential must never fall back to a remembered account.
228+
if (route.query.personalCredentialId !== undefined) {
229+
const credentialId = queryValue(route.query.personalCredentialId);
230+
if (admin || !credentialId) throw new Error(t("ConnectError.PersonalCredentialNotFound"));
231+
232+
const credential = await getPersonalAssetCredential(credentialId, orgId);
233+
if (credential.asset.id !== assetId || !credential.is_active || !credential.has_secret) {
234+
throw new Error(t("ConnectError.PersonalCredentialNotFound"));
235+
}
236+
const protocol = typeof credential.protocol === "string" ? credential.protocol : credential.protocol.value;
237+
if (
238+
!asset.permedProtocols?.some(
239+
(item) => item.name === protocol && (isDesktopRuntime() || item.public !== false)
240+
)
241+
) {
242+
throw new Error(t("ConnectError.ProtocolUnavailable"));
243+
}
244+
if (!asset.permedAccounts?.some((account) => account.alias === "@INPUT")) {
245+
throw new Error(t("ConnectError.ManualAccountDenied"));
246+
}
247+
const preferred =
248+
preference?.protocol === protocol ? preference : saved?.protocol === protocol ? saved : undefined;
249+
const connectMethod = queryValue(route.query.method) || preferred?.connectMethod || "";
250+
const pane = openSession(asset, { protocol, account: credential.username, connectMethod });
251+
await confirmConnection(asset, {
252+
protocol,
253+
account: "@INPUT",
254+
accountMode: "manual",
255+
manualUsername: credential.username,
256+
manualPassword: "",
257+
personalCredentialId: credential.id,
258+
personalCredentialVersion: credential.version,
259+
personalCredentialSecretType:
260+
typeof credential.secret_type === "string" ? credential.secret_type : credential.secret_type.value,
261+
savePersonalCredential: false,
262+
dynamicPassword: "",
263+
rememberSecret: false,
264+
preserveStoredSelection: true,
265+
connectMethod,
266+
connectOptions: preferred?.connectOptions || {},
267+
tabId: pane.id
268+
});
269+
return;
270+
}
271+
217272
const reusableSavedConnection = !admin && hasReusableSavedConnection(asset);
218273
const connection = { ...(saved || {}), ...(preference || {}), ...(routeConnection || {}) };
219274
const queryNeedsNoSecret = routeConnection && ["hosted", "anonymous"].includes(routeConnection.accountMode);

0 commit comments

Comments
 (0)