@@ -43,6 +43,116 @@ type ReqWithSession = Request & {
4343 headers : Record < string , unknown >
4444}
4545
46+ /**
47+ * Handles SSH validation failure responses based on error type
48+ * Pure function that returns response data without side effects
49+ */
50+ function createSshValidationErrorResponse ( validationResult : {
51+ errorType ?: string
52+ errorMessage ?: string
53+ } , host : string , port : number ) : {
54+ status : number
55+ headers ?: Record < string , string >
56+ message : string
57+ } {
58+ switch ( validationResult . errorType ) {
59+ case 'auth' :
60+ // Authentication failed - allow re-authentication
61+ return {
62+ status : HTTP . UNAUTHORIZED ,
63+ headers : { [ HTTP . AUTHENTICATE ] : HTTP . REALM } ,
64+ message : 'SSH authentication failed'
65+ }
66+ case 'network' :
67+ // Network/connectivity issue - no point in re-authenticating
68+ return {
69+ status : 502 ,
70+ message : `Bad Gateway: Unable to connect to SSH server at ${ host } :${ port } - ${ validationResult . errorMessage } `
71+ }
72+ case 'timeout' :
73+ // Connection timeout
74+ return {
75+ status : 504 ,
76+ message : `Gateway Timeout: SSH connection to ${ host } :${ port } timed out`
77+ }
78+ case undefined :
79+ case 'unknown' :
80+ default :
81+ // Unknown error - return 502 as it's likely a connectivity issue
82+ return {
83+ status : 502 ,
84+ message : `Bad Gateway: SSH connection failed - ${ validationResult . errorMessage } `
85+ }
86+ }
87+ }
88+
89+ /**
90+ * Common SSH route handler logic
91+ * Handles validation, authentication, and connection setup
92+ */
93+ async function handleSshRoute (
94+ req : ReqWithSession ,
95+ res : Response ,
96+ connectionParams : {
97+ host : string
98+ port : number
99+ term : string | null
100+ } ,
101+ config : Config
102+ ) : Promise < void > {
103+ // Get credentials from session (set by auth middleware)
104+ const sshCredentials = req . session . sshCredentials
105+ if ( sshCredentials ?. username == null || sshCredentials . username === '' ||
106+ sshCredentials . password == null || sshCredentials . password === '' ) {
107+ debug ( 'Missing SSH credentials in session' )
108+ res . setHeader ( HTTP . AUTHENTICATE , HTTP . REALM )
109+ res . status ( HTTP . UNAUTHORIZED ) . send ( 'Missing SSH credentials' )
110+ return
111+ }
112+
113+ // Validate SSH credentials immediately
114+ const validationResult = await validateSshCredentials (
115+ connectionParams . host ,
116+ connectionParams . port ,
117+ sshCredentials . username ,
118+ sshCredentials . password ,
119+ config
120+ )
121+
122+ if ( validationResult . success === false ) {
123+ debug (
124+ `SSH validation failed for ${ sshCredentials . username } @${ connectionParams . host } :${ connectionParams . port } : ${ validationResult . errorType } - ${ validationResult . errorMessage } `
125+ )
126+
127+ // Get error response data
128+ const errorResponse = createSshValidationErrorResponse (
129+ validationResult ,
130+ connectionParams . host ,
131+ connectionParams . port
132+ )
133+
134+ // Set headers if provided
135+ if ( errorResponse . headers != null ) {
136+ Object . entries ( errorResponse . headers ) . forEach ( ( [ key , value ] ) => {
137+ res . setHeader ( key , value )
138+ } )
139+ }
140+
141+ res . status ( errorResponse . status ) . send ( errorResponse . message )
142+ return
143+ }
144+
145+ // SSH validation succeeded - proceed with normal flow
146+ processAuthParameters ( req . query , req . session )
147+ const sanitizedCredentials = setupSshCredentials ( req . session , connectionParams )
148+ debug ( 'SSH validation passed - serving client: ' , sanitizedCredentials )
149+ void handleConnection (
150+ req as unknown as Request & { session ?: Record < string , unknown > ; sessionID ?: string } ,
151+ res ,
152+ { host : connectionParams . host }
153+ )
154+ }
155+
46156
47157/**
48158 * Validate SSH credentials by attempting a connection
@@ -89,72 +199,7 @@ export function createRoutes(config: Config): Router {
89199 config,
90200 } )
91201
92- // Get credentials from session (set by auth middleware)
93- const sshCredentials = r . session . sshCredentials
94- if ( sshCredentials ?. username == null || sshCredentials . username === '' || sshCredentials . password == null || sshCredentials . password === '' ) {
95- debug ( 'Missing SSH credentials in session' )
96- res . setHeader ( HTTP . AUTHENTICATE , HTTP . REALM )
97- res . status ( HTTP . UNAUTHORIZED ) . send ( 'Missing SSH credentials' )
98- return
99- }
100-
101- // Validate SSH credentials immediately
102- const validationResult = await validateSshCredentials (
103- host ,
104- port ,
105- sshCredentials . username ,
106- sshCredentials . password ,
107- config
108- )
109-
110- if ( validationResult . success === false ) {
111- debug (
112- `SSH validation failed for ${ sshCredentials . username } @${ host } :${ port } : ${ validationResult . errorType } - ${ validationResult . errorMessage } `
113- )
114-
115- // Return appropriate status code based on error type
116- switch ( validationResult . errorType ) {
117- case 'auth' :
118- // Authentication failed - allow re-authentication
119- res . setHeader ( HTTP . AUTHENTICATE , HTTP . REALM )
120- res . status ( HTTP . UNAUTHORIZED ) . send ( 'SSH authentication failed' )
121- break
122- case 'network' :
123- // Network/connectivity issue - no point in re-authenticating
124- res
125- . status ( 502 )
126- . send (
127- `Bad Gateway: Unable to connect to SSH server at ${ host } :${ port } - ${ validationResult . errorMessage } `
128- )
129- break
130- case 'timeout' :
131- // Connection timeout
132- res . status ( 504 ) . send ( `Gateway Timeout: SSH connection to ${ host } :${ port } timed out` )
133- break
134- case undefined :
135- case 'unknown' :
136- default :
137- // Unknown error - return 502 as it's likely a connectivity issue
138- res
139- . status ( 502 )
140- . send ( `Bad Gateway: SSH connection failed - ${ validationResult . errorMessage } ` )
141- }
142- return
143- }
144-
145- // SSH validation succeeded - proceed with normal flow
146- processAuthParameters ( r . query , r . session )
147- const sanitizedCredentials = setupSshCredentials ( r . session , {
148- host,
149- port,
150- term,
151- } )
152- debug ( '/ssh/host/ SSH validation passed - serving client: ' , sanitizedCredentials )
153- void handleConnection (
154- req as unknown as Request & { session ?: Record < string , unknown > ; sessionID ?: string } ,
155- res ,
156- { host }
157- )
202+ await handleSshRoute ( r , res , { host, port, term } , config )
158203 } catch ( err ) {
159204 handleRouteError ( err as Error , res )
160205 }
@@ -175,72 +220,7 @@ export function createRoutes(config: Config): Router {
175220 config,
176221 } )
177222
178- // Get credentials from session (set by auth middleware)
179- const sshCredentials = r . session . sshCredentials
180- if ( sshCredentials ?. username == null || sshCredentials . username === '' || sshCredentials . password == null || sshCredentials . password === '' ) {
181- debug ( 'Missing SSH credentials in session' )
182- res . setHeader ( HTTP . AUTHENTICATE , HTTP . REALM )
183- res . status ( HTTP . UNAUTHORIZED ) . send ( 'Missing SSH credentials' )
184- return
185- }
186-
187- // Validate SSH credentials immediately
188- const validationResult = await validateSshCredentials (
189- host ,
190- port ,
191- sshCredentials . username ,
192- sshCredentials . password ,
193- config
194- )
195-
196- if ( validationResult . success === false ) {
197- debug (
198- `SSH validation failed for ${ sshCredentials . username } @${ host } :${ port } : ${ validationResult . errorType } - ${ validationResult . errorMessage } `
199- )
200-
201- // Return appropriate status code based on error type
202- switch ( validationResult . errorType ) {
203- case 'auth' :
204- // Authentication failed - allow re-authentication
205- res . setHeader ( HTTP . AUTHENTICATE , HTTP . REALM )
206- res . status ( HTTP . UNAUTHORIZED ) . send ( 'SSH authentication failed' )
207- break
208- case 'network' :
209- // Network/connectivity issue - no point in re-authenticating
210- res
211- . status ( 502 )
212- . send (
213- `Bad Gateway: Unable to connect to SSH server at ${ host } :${ port } - ${ validationResult . errorMessage } `
214- )
215- break
216- case 'timeout' :
217- // Connection timeout
218- res . status ( 504 ) . send ( `Gateway Timeout: SSH connection to ${ host } :${ port } timed out` )
219- break
220- case undefined :
221- case 'unknown' :
222- default :
223- // Unknown error - return 502 as it's likely a connectivity issue
224- res
225- . status ( 502 )
226- . send ( `Bad Gateway: SSH connection failed - ${ validationResult . errorMessage } ` )
227- }
228- return
229- }
230-
231- // SSH validation succeeded - proceed with normal flow
232- processAuthParameters ( r . query , r . session )
233- const sanitizedCredentials = setupSshCredentials ( r . session , {
234- host,
235- port,
236- term,
237- } )
238- debug ( '/ssh/host/:host SSH validation passed - serving client: ' , sanitizedCredentials )
239- void handleConnection (
240- req as unknown as Request & { session ?: Record < string , unknown > ; sessionID ?: string } ,
241- res ,
242- { host }
243- )
223+ await handleSshRoute ( r , res , { host, port, term } , config )
244224 } catch ( err ) {
245225 handleRouteError ( err as Error , res )
246226 }
0 commit comments