@@ -9,7 +9,7 @@ use pam::Client;
99use serde:: { Serialize , Deserialize } ;
1010use std:: { collections:: HashMap , sync:: { Arc , Mutex } , time:: { SystemTime , UNIX_EPOCH } } ;
1111use std:: fs;
12- use std:: io:: prelude:: * ;
12+ use std:: io:: { BufRead , BufReader , prelude:: * } ;
1313use std:: os:: unix:: fs:: OpenOptionsExt ;
1414use tokio:: time:: { interval, Duration as TokioDuration } ;
1515use uuid:: Uuid ;
@@ -156,48 +156,39 @@ async fn handle_login(
156156 return Err ( ( StatusCode :: UNAUTHORIZED , "Invalid username or password" ) ) ;
157157 }
158158
159- // Check TOTP
160- let totp_file = fs:: read_to_string ( & ARGS . shadow_file ) . unwrap ( ) ;
161- let mut totp_map = HashMap :: new ( ) ;
162-
163- for ( _, line) in totp_file. lines ( ) . enumerate ( ) {
164- if line. is_empty ( ) {
165- continue
166- }
167- let mut parts = line. splitn ( 2 , ',' ) ;
168- let username = parts. next ( ) . unwrap_or ( "" ) . trim ( ) ;
169- let secret = parts. next ( ) . unwrap_or ( "" ) . trim ( ) ;
159+ // Search TOTP secret in shadow_file
160+ let file = fs:: File :: open ( ARGS . shadow_file . clone ( ) ) . unwrap ( ) ;
161+ let reader = BufReader :: new ( file) ;
162+ for line in reader. lines ( ) {
163+ if let Some ( ( user, secret) ) = line. unwrap ( ) . split_once ( ',' ) {
164+
165+ // Username found
166+ if user == & form. username {
167+ let code: u32 = form. totp . parse ( ) . unwrap_or ( 0 ) ;
168+ let totp = TOTP :: from_base32 ( secret) . unwrap ( ) ;
169+ if !totp. verify ( code, 30 , SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( ) ) {
170+ println ! ( "Login denied: Invalid TOTP for {}" , & form. username) ;
171+ return Err ( ( StatusCode :: UNAUTHORIZED , "Invalid TOTP" ) )
172+ }
170173
171- if !username. is_empty ( ) && !secret. is_empty ( ) {
172- totp_map. insert ( username. to_string ( ) , secret. to_string ( ) ) ;
173- }
174- }
175-
176- let totp_map = Arc :: new ( totp_map) ;
177-
178- if let Some ( user_entry) = totp_map. get ( & form. username ) {
179- let code: u32 = form. totp . parse ( ) . unwrap_or ( 0 ) ;
180- let totp = TOTP :: from_base32 ( user_entry) . unwrap ( ) ;
181- if !totp. verify ( code, 30 , SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( ) ) {
182- println ! ( "Login denied: Invalid TOTP for {}" , & form. username) ;
183- return Err ( ( StatusCode :: UNAUTHORIZED , "Invalid TOTP" ) )
184- }
185-
186- // Create session
187- let session_id = Uuid :: new_v4 ( ) . to_string ( ) ;
188- let expires = Utc :: now ( ) + ARGS . session_lifetime ;
189- sessions. lock ( ) . unwrap ( ) . insert ( session_id. clone ( ) , SessionData { username : form. username . clone ( ) , expires_at : expires } ) ;
174+ // Create session
175+ let session_id = Uuid :: new_v4 ( ) . to_string ( ) ;
176+ let expires = Utc :: now ( ) + ARGS . session_lifetime ;
177+ sessions. lock ( ) . unwrap ( ) . insert ( session_id. clone ( ) , SessionData { username : form. username . clone ( ) , expires_at : expires } ) ;
190178
191- // Persist sessions in file, if enabled
192- save_sessions_to_file ( & sessions) ;
179+ // Persist sessions in file, if enabled
180+ save_sessions_to_file ( & sessions) ;
193181
194- println ! ( "Login successful: {}" , & form. username) ;
195- let offset_dt = OffsetDateTime :: from_unix_timestamp ( expires. timestamp ( ) )
196- . expect ( "valid timestamp" ) ;
197- return Ok ( (
198- jar. add ( Cookie :: build ( ( "nginx-auth" , session_id) ) . path ( "/" ) . http_only ( true ) . expires ( offset_dt) ) ,
199- "Login successful"
200- ) ) ;
182+ println ! ( "Login successful: {}" , & form. username) ;
183+ let offset_dt = OffsetDateTime :: from_unix_timestamp ( expires. timestamp ( ) )
184+ . expect ( "valid timestamp" ) ;
185+
186+ return Ok ( (
187+ jar. add ( Cookie :: build ( ( "nginx-auth" , session_id) ) . path ( "/" ) . http_only ( true ) . expires ( offset_dt) ) ,
188+ "Login successful"
189+ ) ) ;
190+ }
191+ }
201192 }
202193
203194 println ! ( "Login denied: Missing TOTP {}" , & form. username) ;
0 commit comments