Compare commits
2 Commits
91a2aeb3fa
...
4b8396b6a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b8396b6a6 | |||
| 09de033d70 |
@@ -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).
|
- 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.
|
- Enter the **Username** and **Password** generated from the portal.
|
||||||
|
|
||||||
3. **Admin Management**:
|
3. **Admin Management & First-Time Setup**:
|
||||||
- Access the Admin Dashboard at `http://<server-ip>:8080/admin`.
|
- Access the Admin Dashboard at `http://<server-ip>: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.
|
- Monitor active RADIUS sessions, data usage, enforce data limits, or revoke users.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -8,18 +8,93 @@
|
|||||||
<div class="brand-icon" style="margin: 0 auto 1rem auto; width: 60px; height: 60px; font-size: 1.8rem; background: linear-gradient(135deg, var(--accent-purple), var(--primary));">
|
<div class="brand-icon" style="margin: 0 auto 1rem auto; width: 60px; height: 60px; font-size: 1.8rem; background: linear-gradient(135deg, var(--accent-purple), var(--primary));">
|
||||||
<i class="bi bi-shield-lock-fill"></i>
|
<i class="bi bi-shield-lock-fill"></i>
|
||||||
</div>
|
</div>
|
||||||
|
@if (isFirstRun)
|
||||||
|
{
|
||||||
|
<h1 class="portal-title">First-Time Setup</h1>
|
||||||
|
<p class="portal-subtitle">Create your primary administrator account to secure the RADIUS Controller panel</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
<h1 class="portal-title">Administrator Login</h1>
|
<h1 class="portal-title">Administrator Login</h1>
|
||||||
<p class="portal-subtitle">Access RADIUS server policies, session limits, and data accounting</p>
|
<p class="portal-subtitle">Access RADIUS server policies, session limits, and data accounting</p>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="glass-card">
|
<div class="glass-card">
|
||||||
|
@if (isCheckingFirstRun)
|
||||||
|
{
|
||||||
|
<div style="text-align: center; padding: 2rem;">
|
||||||
|
<div class="spinner-border text-primary" role="status"></div>
|
||||||
|
<p style="margin-top: 1rem; color: var(--text-muted); font-size: 0.9rem;">Checking system configuration...</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (isFirstRun)
|
||||||
|
{
|
||||||
|
<div style="background: rgba(20, 184, 166, 0.1); border: 1px solid rgba(20, 184, 166, 0.3); border-radius: 8px; padding: 0.85rem 1rem; margin-bottom: 1.5rem; font-size: 0.85rem; color: var(--accent-teal);">
|
||||||
|
<i class="bi bi-info-circle-fill"></i> <strong>Welcome!</strong> No admin user exists in the database. Please create your administrator credentials below.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditForm Model="this" OnValidSubmit="HandleFirstTimeSetup">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="setupUsername">Administrator Username</label>
|
||||||
|
<input id="setupUsername"
|
||||||
|
type="text"
|
||||||
|
class="form-control-glass"
|
||||||
|
placeholder="e.g. admin"
|
||||||
|
@bind="username"
|
||||||
|
required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="setupPassword">Password</label>
|
||||||
|
<input id="setupPassword"
|
||||||
|
type="password"
|
||||||
|
class="form-control-glass"
|
||||||
|
placeholder="••••••••"
|
||||||
|
@bind="password"
|
||||||
|
required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="confirmPassword">Confirm Password</label>
|
||||||
|
<input id="confirmPassword"
|
||||||
|
type="password"
|
||||||
|
class="form-control-glass"
|
||||||
|
placeholder="••••••••"
|
||||||
|
@bind="confirmPassword"
|
||||||
|
required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(errorMessage))
|
||||||
|
{
|
||||||
|
<div style="color: var(--accent-rose); font-size: 0.875rem; margin-bottom: 1rem;">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill"></i> @errorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<button type="submit" class="btn-gradient-primary" disabled="@isSubmitting">
|
||||||
|
@if (isSubmitting)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span>Creating Administrator Account...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<i class="bi bi-person-check-fill"></i>
|
||||||
|
<span>Create Admin Account & Log In</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</EditForm>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
<EditForm Model="this" OnValidSubmit="HandleLogin">
|
<EditForm Model="this" OnValidSubmit="HandleLogin">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="adminUsername">Username</label>
|
<label class="form-label" for="adminUsername">Username</label>
|
||||||
<input id="adminUsername"
|
<input id="adminUsername"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control-glass"
|
class="form-control-glass"
|
||||||
placeholder="admin"
|
placeholder="Username"
|
||||||
@bind="username"
|
@bind="username"
|
||||||
required />
|
required />
|
||||||
</div>
|
</div>
|
||||||
@@ -54,16 +129,17 @@
|
|||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
|
}
|
||||||
<div style="margin-top: 1.5rem; text-align: center; padding-top: 1rem; border-top: 1px solid var(--bg-card-border); font-size: 0.8rem; color: var(--text-dim);">
|
|
||||||
Default credentials: <code style="color: var(--accent-teal);">admin</code> / <code style="color: var(--accent-teal);">admin123</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string password = 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 bool isSubmitting = false;
|
||||||
private string errorMessage = string.Empty;
|
private string errorMessage = string.Empty;
|
||||||
|
|
||||||
@@ -73,7 +149,60 @@
|
|||||||
if (AuthService.IsAuthenticated)
|
if (AuthService.IsAuthenticated)
|
||||||
{
|
{
|
||||||
NavManager.NavigateTo("/admin");
|
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()
|
private async Task HandleLogin()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using radiuscontroller.Data;
|
using radiuscontroller.Data;
|
||||||
|
using radiuscontroller.Models;
|
||||||
|
|
||||||
namespace radiuscontroller.Services;
|
namespace radiuscontroller.Services;
|
||||||
|
|
||||||
@@ -39,6 +40,39 @@ public class AdminAuthService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasAnyAdminAsync()
|
||||||
|
{
|
||||||
|
return await _db.AdminUsers.AnyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> 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<bool> LoginAsync(string username, string password)
|
public async Task<bool> LoginAsync(string username, string password)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||||
|
|||||||
@@ -331,18 +331,6 @@ public class RadiusService : IRadiusService
|
|||||||
{
|
{
|
||||||
await _db.Database.EnsureCreatedAsync();
|
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())
|
if (!await _db.SystemSettings.AnyAsync())
|
||||||
{
|
{
|
||||||
_db.SystemSettings.Add(new SystemSetting { Key = "default_session_time", Value = "60", UpdatedAt = DateTime.UtcNow });
|
_db.SystemSettings.Add(new SystemSetting { Key = "default_session_time", Value = "60", UpdatedAt = DateTime.UtcNow });
|
||||||
|
|||||||
+2
-4
@@ -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_session_time', '60') ON CONFLICT (key) DO NOTHING;
|
||||||
INSERT INTO system_settings (key, value) VALUES ('default_data_limit', '500') 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)
|
-- System settings initialized on startup
|
||||||
INSERT INTO admin_users (username, password_hash)
|
-- Admin user is created by administrator during first startup via the web interface
|
||||||
VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy')
|
|
||||||
ON CONFLICT (username) DO NOTHING;
|
|
||||||
|
|||||||
Reference in New Issue
Block a user