BTCPay-infrastructure-recovery
This commit is contained in:
@@ -38,20 +38,29 @@ public class AccountController : Controller
|
||||
return View();
|
||||
}
|
||||
|
||||
if (username == "admin" && password == "admin")
|
||||
// Use AuthService to validate against database users
|
||||
var loginDto = new LoginDto { Username = username, Password = password };
|
||||
var authResponse = await _authService.LoginAsync(loginDto);
|
||||
|
||||
if (authResponse != null)
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
// Get the actual user from database to get correct ID
|
||||
var user = await _authService.GetUserByUsernameAsync(username);
|
||||
if (user != null)
|
||||
{
|
||||
new(ClaimTypes.Name, "admin"),
|
||||
new(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),
|
||||
new(ClaimTypes.Role, "Admin")
|
||||
};
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(ClaimTypes.Name, user.Username),
|
||||
new(ClaimTypes.NameIdentifier, user.Id.ToString()), // Use real database ID
|
||||
new(ClaimTypes.Role, "Admin") // All users in admin system are admins
|
||||
};
|
||||
|
||||
var identity = new ClaimsIdentity(claims, "Cookies");
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
var identity = new ClaimsIdentity(claims, "Cookies");
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
|
||||
await HttpContext.SignInAsync("Cookies", principal);
|
||||
return RedirectToAction("Index", "Dashboard");
|
||||
await HttpContext.SignInAsync("Cookies", principal);
|
||||
return RedirectToAction("Index", "Dashboard");
|
||||
}
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "Invalid username or password");
|
||||
|
||||
@@ -30,19 +30,28 @@ public class UsersController : Controller
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create(CreateUserDto model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _authService.CreateUserAsync(model);
|
||||
if (user == null)
|
||||
{
|
||||
ModelState.AddModelError("Username", "User with this username already exists");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
TempData["SuccessMessage"] = $"User '{user.Username}' created successfully";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", "An error occurred while creating the user: " + ex.Message);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _authService.CreateUserAsync(model);
|
||||
if (user == null)
|
||||
{
|
||||
ModelState.AddModelError("", "User with this username already exists");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
@@ -66,25 +75,89 @@ public class UsersController : Controller
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Edit(Guid id, UpdateUserDto model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
try
|
||||
{
|
||||
// Additional validation for required username
|
||||
if (string.IsNullOrWhiteSpace(model.Username))
|
||||
{
|
||||
ModelState.AddModelError("Username", "Username is required");
|
||||
}
|
||||
|
||||
// Validate password if provided
|
||||
if (!string.IsNullOrEmpty(model.Password) && model.Password.Length < 3)
|
||||
{
|
||||
ModelState.AddModelError("Password", "Password must be at least 3 characters if changing");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
ViewData["UserId"] = id;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var success = await _authService.UpdateUserAsync(id, model);
|
||||
if (!success)
|
||||
{
|
||||
// Check if it's because of duplicate username
|
||||
var existingUser = await _authService.GetUserByIdAsync(id);
|
||||
if (existingUser == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
ModelState.AddModelError("Username", "Username is already taken by another user");
|
||||
ViewData["UserId"] = id;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
TempData["SuccessMessage"] = "User updated successfully";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", "An error occurred while updating the user: " + ex.Message);
|
||||
ViewData["UserId"] = id;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var success = await _authService.UpdateUserAsync(id, model);
|
||||
if (!success)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Delete(Guid id)
|
||||
{
|
||||
await _authService.DeleteUserAsync(id);
|
||||
return RedirectToAction(nameof(Index));
|
||||
try
|
||||
{
|
||||
// Prevent admin user from deleting themselves
|
||||
var currentUserIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
if (Guid.TryParse(currentUserIdClaim, out Guid currentUserId) && currentUserId == id)
|
||||
{
|
||||
TempData["ErrorMessage"] = "You cannot delete your own account";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// Get user info for confirmation message
|
||||
var user = await _authService.GetUserByIdAsync(id);
|
||||
if (user == null)
|
||||
{
|
||||
TempData["ErrorMessage"] = "User not found";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
var success = await _authService.DeleteUserAsync(id);
|
||||
if (success)
|
||||
{
|
||||
TempData["SuccessMessage"] = $"User '{user.Username}' has been deactivated";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = "Failed to delete user";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["ErrorMessage"] = "An error occurred while deleting the user: " + ex.Message;
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
82
LittleShop/Areas/Admin/Views/Users/Edit.cshtml
Normal file
82
LittleShop/Areas/Admin/Views/Users/Edit.cshtml
Normal file
@@ -0,0 +1,82 @@
|
||||
@model LittleShop.DTOs.UpdateUserDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Edit User";
|
||||
var userId = ViewData["UserId"] as Guid?;
|
||||
}
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h1><i class="fas fa-user-edit"></i> Edit User</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post" asp-area="Admin" asp-controller="Users" asp-action="Edit" asp-route-id="@userId">
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
@if (ViewData.ModelState[""] != null && ViewData.ModelState[""].Errors.Count > 0)
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
@foreach (var error in ViewData.ModelState[""].Errors)
|
||||
{
|
||||
<div>@error.ErrorMessage</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Username" class="form-label">Username</label>
|
||||
<input name="Username" id="Username" class="form-control" value="@Model?.Username" required />
|
||||
<div class="form-text">Must be unique across all users</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Password" class="form-label">New Password</label>
|
||||
<input name="Password" id="Password" type="password" class="form-control" />
|
||||
<div class="form-text">Leave blank to keep current password. Minimum 3 characters if changing.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input name="IsActive" id="IsActive" type="checkbox" class="form-check-input" value="true" @(Model?.IsActive == true ? "checked" : "") />
|
||||
<input name="IsActive" type="hidden" value="false" />
|
||||
<label for="IsActive" class="form-check-label">
|
||||
User is active (can log in)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="@Url.Action("Index")" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Users
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Update User
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5><i class="fas fa-info-circle"></i> Edit Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Username:</strong> Can be changed if unique</li>
|
||||
<li><strong>Password:</strong> Optional - leave blank to keep current</li>
|
||||
<li><strong>Status:</strong> Inactive users cannot log in</li>
|
||||
</ul>
|
||||
<div class="alert alert-warning mt-3">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Warning:</strong> Deactivating your own account will lock you out.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,6 +15,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (TempData["SuccessMessage"] != null)
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle"></i> @TempData["SuccessMessage"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (TempData["ErrorMessage"] != null)
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-exclamation-circle"></i> @TempData["ErrorMessage"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@if (Model.Any())
|
||||
|
||||
Reference in New Issue
Block a user