Store users: Ensure the last owner cannot be downgraded (#6654)

* Store users: Ensure the last owner cannot be downgraded

Changes the behaviour of the `AddOrUpdateStoreUser` method to throw errors for the failure cases, so that the UI and API can report the actual problem. A role change might fail if the user already has that role or if they are the last owner of the store.

* Cleanup code

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
d11n
2025-04-08 10:21:07 +02:00
committed by GitHub
parent ce83e4d96d
commit 1c921030dc
7 changed files with 93 additions and 34 deletions

View File

@@ -14,6 +14,7 @@ using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using static BTCPayServer.Services.Stores.StoreRepository;
namespace BTCPayServer.Controllers;
@@ -83,7 +84,9 @@ public partial class UIStoresController
var action = isExistingUser
? isExistingStoreUser ? "updated" : "added"
: "invited";
if (await _storeRepo.AddOrUpdateStoreUser(CurrentStore.Id, user.Id, roleId))
var res = await _storeRepo.AddOrUpdateStoreUser(CurrentStore.Id, user.Id, roleId);
if (res is AddOrUpdateStoreUserResult.Success)
{
TempData.SetStatusMessageModel(new StatusMessageModel
{
@@ -93,9 +96,11 @@ public partial class UIStoresController
});
return RedirectToAction(nameof(StoreUsers));
}
ModelState.AddModelError(nameof(vm.Email), $"The user could not be {action}");
return View(vm);
else
{
ModelState.AddModelError(nameof(vm.Email), $"The user could not be {action}: {res.ToString()}");
return View(vm);
}
}
[HttpPost("{storeId}/users/{userId}")]
@@ -105,12 +110,16 @@ public partial class UIStoresController
var roleId = await _storeRepo.ResolveStoreRoleId(storeId, vm.Role);
var storeUsers = await _storeRepo.GetStoreUsers(storeId);
var user = storeUsers.First(user => user.Id == userId);
var isOwner = user.StoreRole.Id == StoreRoleId.Owner.Id;
var isLastOwner = isOwner && storeUsers.Count(u => u.StoreRole.Id == StoreRoleId.Owner.Id) == 1;
if (isLastOwner && roleId != StoreRoleId.Owner)
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["User {0} is the last owner. Their role cannot be changed.", user.Email].Value;
else if (await _storeRepo.AddOrUpdateStoreUser(storeId, userId, roleId))
var res = await _storeRepo.AddOrUpdateStoreUser(storeId, userId, roleId);
if (res is AddOrUpdateStoreUserResult.Success)
{
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["The role of {0} has been changed to {1}.", user.Email, vm.Role].Value;
}
else
{
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Changing the role of user {0} failed: {1}", user.Email, res.ToString()].Value;
}
return RedirectToAction(nameof(StoreUsers), new { storeId, userId });
}