11import 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" ;
38import { desktopInvoke } from "~/shared/desktop/bridge" ;
49import { useUserInfoStore } from "~/store/modules/userInfo" ;
510import { 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
163172export 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