diff --git a/README.md b/README.md index 121b4d5..6d21861 100644 --- a/README.md +++ b/README.md @@ -148,9 +148,9 @@ VALUES ('192.168.1.1', 'unifi-gw', 'other', 'YourSharedSecret123', 'UniFi Gatewa - CA Certificate: `Do not validate` / `Unvalidated` (or install your custom CA if using custom EAP certs). - Enter the **Username** and **Password** generated from the portal. -3. **Admin Management**: +3. **Admin Management & First-Time Setup**: - Access the Admin Dashboard at `http://:8080/admin`. - - Default login: `admin` / `admin123`. + - **First-Time Setup**: On initial startup, accessing the admin panel automatically presents the setup screen to create your custom administrator username and password. - Monitor active RADIUS sessions, data usage, enforce data limits, or revoke users. --- diff --git a/radiuscontroller/Components/Pages/Admin/Login.razor b/radiuscontroller/Components/Pages/Admin/Login.razor index ee3ec5c..a700626 100644 --- a/radiuscontroller/Components/Pages/Admin/Login.razor +++ b/radiuscontroller/Components/Pages/Admin/Login.razor @@ -8,62 +8,138 @@
-

Administrator Login

-

Access RADIUS server policies, session limits, and data accounting

+ @if (isFirstRun) + { +

First-Time Setup

+

Create your primary administrator account to secure the RADIUS Controller panel

+ } + else + { +

Administrator Login

+

Access RADIUS server policies, session limits, and data accounting

+ }
- -
- - + @if (isCheckingFirstRun) + { +
+
+

Checking system configuration...

+
+ } + else if (isFirstRun) + { +
+ Welcome! No admin user exists in the database. Please create your administrator credentials below.
-
- - -
- - @if (!string.IsNullOrEmpty(errorMessage)) - { -
- @errorMessage + +
+ +
- } - -
+
+ + +
-
- Default credentials: admin / admin123 -
+
+ + +
+ + @if (!string.IsNullOrEmpty(errorMessage)) + { +
+ @errorMessage +
+ } + + + + } + else + { + +
+ + +
+ +
+ + +
+ + @if (!string.IsNullOrEmpty(errorMessage)) + { +
+ @errorMessage +
+ } + + +
+ }
@code { private string username = string.Empty; private string password = string.Empty; + private string confirmPassword = string.Empty; + + private bool isCheckingFirstRun = true; + private bool isFirstRun = false; private bool isSubmitting = false; private string errorMessage = string.Empty; @@ -73,7 +149,60 @@ if (AuthService.IsAuthenticated) { NavManager.NavigateTo("/admin"); + return; } + + try + { + isFirstRun = !await AuthService.HasAnyAdminAsync(); + } + catch + { + isFirstRun = false; + } + finally + { + isCheckingFirstRun = false; + } + } + + private async Task HandleFirstTimeSetup() + { + isSubmitting = true; + errorMessage = string.Empty; + + if (string.IsNullOrWhiteSpace(username) || username.Trim().Length < 3) + { + errorMessage = "Username must be at least 3 characters long."; + isSubmitting = false; + return; + } + + if (string.IsNullOrWhiteSpace(password) || password.Length < 6) + { + errorMessage = "Password must be at least 6 characters long."; + isSubmitting = false; + return; + } + + if (password != confirmPassword) + { + errorMessage = "Password and Confirm Password do not match."; + isSubmitting = false; + return; + } + + bool success = await AuthService.RegisterInitialAdminAsync(username, password); + if (success) + { + NavManager.NavigateTo("/admin"); + } + else + { + errorMessage = "Failed to create administrator account. An admin user may already exist."; + } + + isSubmitting = false; } private async Task HandleLogin() diff --git a/radiuscontroller/Services/AdminAuthService.cs b/radiuscontroller/Services/AdminAuthService.cs index c9370f1..7ef3e52 100644 --- a/radiuscontroller/Services/AdminAuthService.cs +++ b/radiuscontroller/Services/AdminAuthService.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; using Microsoft.EntityFrameworkCore; using radiuscontroller.Data; +using radiuscontroller.Models; namespace radiuscontroller.Services; @@ -39,6 +40,39 @@ public class AdminAuthService } } + public async Task HasAnyAdminAsync() + { + return await _db.AdminUsers.AnyAsync(); + } + + public async Task RegisterInitialAdminAsync(string username, string password) + { + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) + return false; + + // Ensure first-time setup is only allowed if no admin users exist + if (await _db.AdminUsers.AnyAsync()) + return false; + + string cleanUsername = username.Trim().ToLowerInvariant(); + string hash = BCrypt.Net.BCrypt.HashPassword(password.Trim()); + + var admin = new AdminUser + { + Username = cleanUsername, + PasswordHash = hash, + CreatedAt = DateTime.UtcNow + }; + + _db.AdminUsers.Add(admin); + await _db.SaveChangesAsync(); + + CurrentAdminUsername = admin.Username; + await _sessionStorage.SetAsync(SessionKey, admin.Username); + NotifyStateChanged(); + return true; + } + public async Task LoginAsync(string username, string password) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) diff --git a/radiuscontroller/Services/RadiusService.cs b/radiuscontroller/Services/RadiusService.cs index 927cc07..442a0fb 100644 --- a/radiuscontroller/Services/RadiusService.cs +++ b/radiuscontroller/Services/RadiusService.cs @@ -331,18 +331,6 @@ public class RadiusService : IRadiusService { await _db.Database.EnsureCreatedAsync(); - if (!await _db.AdminUsers.AnyAsync()) - { - // Default admin user: admin / admin123 - string hash = BCrypt.Net.BCrypt.HashPassword("admin123"); - _db.AdminUsers.Add(new AdminUser - { - Username = "admin", - PasswordHash = hash, - CreatedAt = DateTime.UtcNow - }); - } - if (!await _db.SystemSettings.AnyAsync()) { _db.SystemSettings.Add(new SystemSetting { Key = "default_session_time", Value = "60", UpdatedAt = DateTime.UtcNow }); diff --git a/sql/init.sql b/sql/init.sql index 9ae7402..f67c8bf 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -114,7 +114,5 @@ CREATE TABLE IF NOT EXISTS system_settings ( INSERT INTO system_settings (key, value) VALUES ('default_session_time', '60') ON CONFLICT (key) DO NOTHING; INSERT INTO system_settings (key, value) VALUES ('default_data_limit', '500') ON CONFLICT (key) DO NOTHING; --- Seed Default Admin User: admin / admin123 (BCrypt hash) -INSERT INTO admin_users (username, password_hash) -VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy') -ON CONFLICT (username) DO NOTHING; +-- System settings initialized on startup +-- Admin user is created by administrator during first startup via the web interface