Files
tiku-backend.net/Tiku.Infrastructure/PlatformAdmin/StaffAndAccess/PlatformStaffAccessService.cs

137 lines
7.0 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Identity;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
namespace Tiku.Infrastructure.PlatformAdmin;
internal sealed class PlatformStaffAccessService(PlatformAdministrationDependencies dependencies)
: PlatformAdministrationServiceBase(dependencies), IPlatformStaffAccessService
{
public async Task<PlatformStaffList> GetStaffAsync(
PlatformAdminActor actor,
PlatformAdminQuery query,
CancellationToken cancellationToken = default)
{
await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken);
return await ExecuteSystemAsync("platform staff list", async dbContext =>
{
var roleRows = await (
from userRole in dbContext.PlatformBackendUserRoles.AsNoTracking()
join role in dbContext.PlatformBackendRoles.AsNoTracking() on userRole.RoleId equals role.Id
join user in dbContext.Users.AsNoTracking() on userRole.UserId equals user.Id
select new { user, role.Code })
.ToArrayAsync(cancellationToken);
var items = roleRows
.GroupBy(row => row.user.Id)
.Select(group => ToStaffItem(group.First().user,
group.Select(row => row.Code).Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal)
.ToArray()))
.OrderBy(item => item.Email ?? item.PhoneMasked ?? item.UserId.ToString())
.Take(Limit(query.Limit))
.ToArray();
return new PlatformStaffList(items);
}, cancellationToken);
}
public async Task<PlatformStaffItem> UpsertStaffAsync(
PlatformAdminActor actor,
UpsertPlatformStaffCommand command,
CancellationToken cancellationToken = default)
{
await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken);
return await ExecuteSystemAsync("platform staff upsert", async (provider, dbContext) =>
{
var user = command.UserId.HasValue
? await dbContext.Users.SingleOrDefaultAsync(item => item.Id == command.UserId.Value, cancellationToken)
: await dbContext.Users.SingleOrDefaultAsync(item =>
(!string.IsNullOrWhiteSpace(command.Email) && item.Email == command.Email) ||
(!string.IsNullOrWhiteSpace(command.Phone) && item.Phone == command.Phone),
cancellationToken);
if (user is null)
{
user = new User
{
Email = Normalize(command.Email),
NormalizedEmail = Normalize(command.Email)?.ToUpperInvariant(),
UserName = Normalize(command.Email) ?? Normalize(command.Phone),
NormalizedUserName = (Normalize(command.Email) ?? Normalize(command.Phone))?.ToUpperInvariant(),
Phone = Normalize(command.Phone),
PhoneNumber = Normalize(command.Phone),
Name = Normalize(command.Name),
PrimaryRole = "platform_admin",
Status = command.Status,
ForcePasswordChange = true
};
dbContext.Users.Add(user);
}
else
{
user.Email = Normalize(command.Email) ?? user.Email;
user.NormalizedEmail = user.Email?.ToUpperInvariant();
user.Phone = Normalize(command.Phone) ?? user.Phone;
user.PhoneNumber = user.Phone;
user.Name = Normalize(command.Name) ?? user.Name;
user.Status = command.Status;
user.PrimaryRole = "platform_admin";
}
var roleIds = command.RoleIds.Distinct().ToArray();
var roleCount =
await dbContext.PlatformBackendRoles.CountAsync(role => roleIds.Contains(role.Id), cancellationToken);
if (roleCount != roleIds.Length)
throw new PlatformAdminException("One or more platform roles were not found.", "role_not_found");
await dbContext.SaveChangesAsync(cancellationToken);
await dbContext.PlatformBackendUserRoles.Where(binding => binding.UserId == user.Id)
.ExecuteDeleteAsync(cancellationToken);
dbContext.PlatformBackendUserRoles.AddRange(roleIds.Select(roleId => new PlatformBackendUserRole
{
UserId = user.Id,
RoleId = roleId
}));
AddAudit(dbContext, actor, "platform.staff.upserted", user.Id,
new { user.Email, Phone = MaskPhone(user.Phone), user.Status, RoleIds = roleIds });
await dbContext.SaveChangesAsync(cancellationToken);
var invalidator = provider.GetRequiredService<IAuthorizationStateInvalidator>();
await invalidator.InvalidateUserAsync(user.Id, cancellationToken);
await invalidator.BumpScopeAsync(AuthRealm.Platform, null, cancellationToken);
var roleCodes = await dbContext.PlatformBackendRoles.AsNoTracking()
.Where(role => roleIds.Contains(role.Id))
.Select(role => role.Code)
.ToArrayAsync(cancellationToken);
return ToStaffItem(user, roleCodes);
}, cancellationToken);
}
public async Task<PlatformStaffItem> UpdateStaffStatusAsync(
PlatformAdminActor actor,
UpdatePlatformStaffStatusCommand command,
CancellationToken cancellationToken = default)
{
await AssertPlatformPermissionAsync(actor, BackendPermissions.PlatformStaffManage, cancellationToken);
return await ExecuteSystemAsync("platform staff status update", async (provider, dbContext) =>
{
var user = await dbContext.Users.SingleOrDefaultAsync(item => item.Id == command.UserId, cancellationToken)
?? throw new PlatformAdminException("Platform staff user was not found.", "staff_not_found");
var from = user.Status;
user.Status = command.Status;
AddAudit(dbContext, actor, "platform.staff.status_changed", user.Id,
new { From = from, To = command.Status, command.Reason });
await dbContext.SaveChangesAsync(cancellationToken);
await provider.GetRequiredService<IAuthorizationStateInvalidator>()
.InvalidateUserAsync(user.Id, cancellationToken);
var roleCodes = await (
from binding in dbContext.PlatformBackendUserRoles.AsNoTracking()
join role in dbContext.PlatformBackendRoles.AsNoTracking() on binding.RoleId equals role.Id
where binding.UserId == user.Id
select role.Code)
.ToArrayAsync(cancellationToken);
return ToStaffItem(user, roleCodes);
}, cancellationToken);
}
}