feat: initialize database schema and implement core Radius entities, EF context, and account enforcement service.
Build and Push Docker Image to Gitea Container Registry / build-and-push (push) Successful in 1m12s

This commit is contained in:
Tygozwolle
2026-07-24 17:51:04 +02:00
parent 9c2cbfa7b9
commit 63b9a97cdf
5 changed files with 126 additions and 14 deletions
@@ -56,6 +56,7 @@ public class AccountEnforcementService : BackgroundService
var now = DateTime.UtcNow;
var disabledCount = 0;
var changesMade = false;
foreach (var profile in activeProfiles)
{
@@ -68,12 +69,11 @@ public class AccountEnforcementService : BackgroundService
reason = "Time expired";
}
// 2. Check data cap: sum acctinputoctets + acctoutputoctets from radacct
// 2. Check data cap: read from guest_data_usage table
if (reason == null && profile.DataLimitMb > 0)
{
var totalOctets = await db.RadAcct
.Where(ra => ra.Username == profile.Username)
.SumAsync(ra => (ra.AcctInputOctets ?? 0) + (ra.AcctOutputOctets ?? 0), ct);
var usage = await db.GuestDataUsage.FirstOrDefaultAsync(du => du.Username == profile.Username, ct);
var totalOctets = (usage?.TotalInputOctets ?? 0) + (usage?.TotalOutputOctets ?? 0);
long dataLimitBytes = profile.DataLimitMb * 1024 * 1024;
if (totalOctets >= dataLimitBytes)
@@ -102,18 +102,57 @@ public class AccountEnforcementService : BackgroundService
Value = "Reject"
});
_logger.LogInformation(
"Account '{Username}' disabled. Reason: {Reason}",
profile.Username, reason);
_logger.LogInformation("Account '{Username}' disabled. Reason: {Reason}", profile.Username, reason);
// Find active sessions to disconnect them instantly via RADIUS CoA (PoD)
var activeSessions = await db.RadAcct
.Where(ra => ra.Username == profile.Username && ra.AcctStopTime == null)
.Select(ra => ra.NasIpAddress)
.Distinct()
.ToListAsync(ct);
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={profile.Username}' | radclient -x {nasIp}:3799 disconnect '{radiusSecret}'\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
await process.WaitForExitAsync(ct);
_logger.LogInformation("Sent RADIUS Disconnect-Request to NAS {NasIp} for user {Username}", nasIp, profile.Username);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send RADIUS Disconnect-Request to NAS {NasIp}", nasIp);
}
}
disabledCount++;
changesMade = true;
}
}
if (disabledCount > 0)
if (changesMade)
{
await db.SaveChangesAsync(ct);
_logger.LogInformation("Enforcement check complete. {Count} account(s) disabled.", disabledCount);
if (disabledCount > 0)
{
_logger.LogInformation("Enforcement check complete. {Count} account(s) disabled.", disabledCount);
}
}
}
}