@@ -31,6 +31,7 @@ use super::auth::AuthProvider;
3131use super :: config:: ServerConfig ;
3232use super :: exec:: CommandExecutor ;
3333use super :: session:: { ChannelState , PtyConfig , SessionId , SessionInfo , SessionManager } ;
34+ use super :: sftp:: SftpHandler ;
3435use crate :: shared:: rate_limit:: RateLimiter ;
3536
3637/// SSH handler for a single client connection.
@@ -186,11 +187,13 @@ impl russh::server::Handler for SshHandler {
186187 let channel_id = channel. id ( ) ;
187188 tracing:: debug!(
188189 peer = ?self . peer_addr,
190+ channel = ?channel_id,
189191 "Channel opened for session"
190192 ) ;
191193
194+ // Store the channel itself so we can use it for subsystems like SFTP
192195 self . channels
193- . insert ( channel_id, ChannelState :: new ( channel_id ) ) ;
196+ . insert ( channel_id, ChannelState :: with_channel ( channel ) ) ;
194197 async { Ok ( true ) }
195198 }
196199
@@ -626,7 +629,8 @@ impl russh::server::Handler for SshHandler {
626629
627630 /// Handle subsystem request.
628631 ///
629- /// Placeholder implementation - will be implemented in a future issue.
632+ /// Handles SFTP subsystem requests by creating an SftpHandler and running
633+ /// the SFTP server on the channel stream.
630634 fn subsystem_request (
631635 & mut self ,
632636 channel_id : ChannelId ,
@@ -635,19 +639,106 @@ impl russh::server::Handler for SshHandler {
635639 ) -> impl std:: future:: Future < Output = Result < ( ) , Self :: Error > > + Send {
636640 tracing:: debug!(
637641 subsystem = %name,
642+ channel = ?channel_id,
643+ peer = ?self . peer_addr,
638644 "Subsystem request"
639645 ) ;
640646
647+ // Handle SFTP subsystem
641648 if name == "sftp" {
642- if let Some ( channel_state) = self . channels . get_mut ( & channel_id) {
643- channel_state. set_sftp ( ) ;
649+ // Check if SFTP is enabled (default: enabled)
650+ // In future, this should check config.sftp.enabled
651+
652+ // Get the channel from our stored channels
653+ let channel = self . channels . get_mut ( & channel_id) . and_then ( |state| {
654+ state. set_sftp ( ) ;
655+ state. take_channel ( )
656+ } ) ;
657+
658+ let channel = match channel {
659+ Some ( ch) => ch,
660+ None => {
661+ tracing:: warn!(
662+ channel = ?channel_id,
663+ "SFTP request but channel not found or already taken"
664+ ) ;
665+ let _ = session. channel_failure ( channel_id) ;
666+ return async { Ok ( ( ) ) } . boxed ( ) ;
667+ }
668+ } ;
669+
670+ // Get authenticated user info
671+ let username = match self . session_info . as_ref ( ) . and_then ( |s| s. user . clone ( ) ) {
672+ Some ( user) => user,
673+ None => {
674+ tracing:: warn!(
675+ channel = ?channel_id,
676+ "SFTP request without authenticated user"
677+ ) ;
678+ let _ = session. channel_failure ( channel_id) ;
679+ return async { Ok ( ( ) ) } . boxed ( ) ;
680+ }
681+ } ;
682+
683+ // Clone what we need for the async block
684+ let auth_provider = Arc :: clone ( & self . auth_provider ) ;
685+ let peer_addr = self . peer_addr ;
686+
687+ // Signal success before spawning the SFTP handler
688+ let _ = session. channel_success ( channel_id) ;
689+
690+ return async move {
691+ // Get user info from auth provider
692+ let user_info = match auth_provider. get_user_info ( & username) . await {
693+ Ok ( Some ( info) ) => info,
694+ Ok ( None ) => {
695+ tracing:: error!(
696+ user = %username,
697+ "User not found after authentication for SFTP"
698+ ) ;
699+ return Ok ( ( ) ) ;
700+ }
701+ Err ( e) => {
702+ tracing:: error!(
703+ user = %username,
704+ error = %e,
705+ "Failed to get user info for SFTP"
706+ ) ;
707+ return Ok ( ( ) ) ;
708+ }
709+ } ;
710+
711+ tracing:: info!(
712+ user = %username,
713+ peer = ?peer_addr,
714+ home = %user_info. home_dir. display( ) ,
715+ "Starting SFTP session"
716+ ) ;
717+
718+ // Create SFTP handler with user's home directory as root
719+ let sftp_handler = SftpHandler :: new ( user_info. clone ( ) , Some ( user_info. home_dir ) ) ;
720+
721+ // Run SFTP server on the channel stream
722+ russh_sftp:: server:: run ( channel. into_stream ( ) , sftp_handler) . await ;
723+
724+ tracing:: info!(
725+ user = %username,
726+ peer = ?peer_addr,
727+ "SFTP session ended"
728+ ) ;
729+
730+ Ok ( ( ) )
644731 }
732+ . boxed ( ) ;
645733 }
646734
647- // Placeholder - reject for now
648- // Will be implemented in #132 for SFTP
735+ // Unknown subsystem - reject
736+ tracing:: debug!(
737+ subsystem = %name,
738+ "Unknown subsystem, rejecting"
739+ ) ;
649740 let _ = session. channel_failure ( channel_id) ;
650- async { Ok ( ( ) ) }
741+ async { Ok ( ( ) ) } . boxed ( )
651742 }
652743
653744 /// Handle incoming data from the client.
0 commit comments