diff --git a/radiuscontroller/Services/RadiusService.cs b/radiuscontroller/Services/RadiusService.cs index 5bb14c1..425f89b 100644 --- a/radiuscontroller/Services/RadiusService.cs +++ b/radiuscontroller/Services/RadiusService.cs @@ -106,13 +106,14 @@ public class RadiusService : IRadiusService Value = password }); - // Set Session-Timeout to 60 seconds to force frequent re-authentication (validation) + // Set Session-Timeout to the full allowed time. When this expires, the AP will drop the user natively. + long sessionTimeoutSeconds = (long)config.DefaultSessionTimeMinutes * 60; _db.RadReply.Add(new RadReply { Username = username, Attribute = "Session-Timeout", Op = "=", - Value = "60" + Value = sessionTimeoutSeconds.ToString() }); // Set Max-Octets (in bytes) @@ -134,14 +135,7 @@ public class RadiusService : IRadiusService Value = "60" }); - // Re-authenticate (instead of disconnect) when Session-Timeout fires - _db.RadReply.Add(new RadReply - { - Username = username, - Attribute = "Termination-Action", - Op = "=", - Value = "1" - }); + // Create Guest Profile record var profile = new GuestProfile @@ -233,11 +227,11 @@ public class RadiusService : IRadiusService profile.SessionTimeMinutes = newSessionTimeMinutes; profile.DataLimitMb = newDataLimitMb; - // Ensure Session-Timeout is strictly 60 seconds for frequent validation + // Update RadReply Session-Timeout to the full allowed time var sessionReply = await _db.RadReply.FirstOrDefaultAsync(rr => rr.Username == profile.Username && rr.Attribute == "Session-Timeout"); if (sessionReply != null) { - sessionReply.Value = "60"; + sessionReply.Value = (newSessionTimeMinutes * 60).ToString(); } else { @@ -246,7 +240,7 @@ public class RadiusService : IRadiusService Username = profile.Username, Attribute = "Session-Timeout", Op = "=", - Value = "60" + Value = (newSessionTimeMinutes * 60).ToString() }); } @@ -291,6 +285,9 @@ public class RadiusService : IRadiusService }); await _db.SaveChangesAsync(); + + // Disconnect active sessions immediately + await DisconnectActiveSessionsAsync(profile.Username); } public async Task DeleteUserAsync(int profileId) @@ -308,6 +305,9 @@ public class RadiusService : IRadiusService _db.GuestProfiles.Remove(profile); await _db.SaveChangesAsync(); + + // Disconnect active sessions immediately + await DisconnectActiveSessionsAsync(username); } public async Task ResetUserPasswordAsync(int profileId, string newPassword) @@ -374,4 +374,43 @@ public class RadiusService : IRadiusService } return new string(result); } + + private async Task DisconnectActiveSessionsAsync(string username) + { + var activeSessions = await _db.RadAcct + .Where(ra => ra.Username == username && ra.AcctStopTime == null) + .Select(ra => ra.NasIpAddress) + .Distinct() + .ToListAsync(); + + var radiusSecret = Environment.GetEnvironmentVariable("RADIUS_SECRET") ?? "radpass"; + + foreach (var nasIp in activeSessions) + { + if (string.IsNullOrWhiteSpace(nasIp)) continue; + + try + { + var process = new System.Diagnostics.Process + { + StartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = "sh", + Arguments = $"-c \"echo 'User-Name={username}' | radclient -x {nasIp}:3799 disconnect '{radiusSecret}'\"", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + process.Start(); + await process.WaitForExitAsync(); + _logger.LogInformation("Sent manual RADIUS Disconnect-Request to NAS {NasIp} for user {Username}", nasIp, username); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to send manual RADIUS Disconnect-Request to NAS {NasIp}", nasIp); + } + } + } }