mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 21:13:12 +00:00
# Conflicts: # Api.SeaHavenIndustries.Tests/TeamMemberServiceTests.cs # SeaHaven.DataServices/Implementation/TeamPermissionOverrideDataService.cs
124 lines
4.1 KiB
C#
124 lines
4.1 KiB
C#
using Data.SeaHavenIndustries;
|
|
using Data.SeaHavenIndustries.Enums;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using SeaHaven.DataServices.Dto;
|
|
using SeaHaven.DataServices.Interfaces;
|
|
|
|
namespace SeaHaven.DataServices.Implementation;
|
|
|
|
public sealed class TeamPermissionOverrideDataService : ITeamPermissionOverrideDataService
|
|
{
|
|
private readonly ApplicationDbContext _context;
|
|
|
|
public TeamPermissionOverrideDataService(ApplicationDbContext context)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
public async Task<TeamPermissionUserData?> GetUserAsync(
|
|
string userId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var userExists = await _context.Users
|
|
.AsNoTracking()
|
|
.AnyAsync(user => user.Id == userId, cancellationToken);
|
|
|
|
if (!userExists)
|
|
return null;
|
|
|
|
var roleName = await (
|
|
from userRole in _context.UserRoles.AsNoTracking()
|
|
join role in _context.Roles.AsNoTracking()
|
|
on userRole.RoleId equals role.Id
|
|
where userRole.UserId == userId
|
|
select role.Name)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
var overrides = await _context.UserPermissionOverrides
|
|
.AsNoTracking()
|
|
.Where(permission => permission.UserId == userId)
|
|
.ToDictionaryAsync(
|
|
permission => permission.PermissionKey,
|
|
permission => permission.State,
|
|
StringComparer.OrdinalIgnoreCase,
|
|
cancellationToken);
|
|
|
|
return new TeamPermissionUserData
|
|
{
|
|
UserId = userId,
|
|
RoleName = roleName,
|
|
Overrides = overrides
|
|
};
|
|
}
|
|
|
|
public Task SetOverrideAsync(
|
|
string userId,
|
|
string permissionKey,
|
|
UserPermissionState state,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return SetOverrideCoreAsync(userId, permissionKey, state, cancellationToken);
|
|
}
|
|
|
|
public async Task SetOverridesAsync(
|
|
string userId,
|
|
IReadOnlyDictionary<string, UserPermissionState> overrides,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
foreach (var pair in overrides)
|
|
await SetOverrideCoreAsync(userId, pair.Key, pair.Value, cancellationToken);
|
|
}
|
|
|
|
private async Task SetOverrideCoreAsync(
|
|
string userId,
|
|
string permissionKey,
|
|
UserPermissionState state,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var existing = await _context.UserPermissionOverrides
|
|
.SingleOrDefaultAsync(
|
|
permission => permission.UserId == userId
|
|
&& permission.PermissionKey == permissionKey,
|
|
cancellationToken);
|
|
|
|
if (existing is not null)
|
|
{
|
|
existing.State = state;
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
return;
|
|
}
|
|
|
|
var addition = new UserPermissionOverride
|
|
{
|
|
UserId = userId,
|
|
PermissionKey = permissionKey,
|
|
State = state
|
|
};
|
|
await _context.UserPermissionOverrides.AddAsync(addition, cancellationToken);
|
|
|
|
try
|
|
{
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
catch (DbUpdateException)
|
|
{
|
|
// A concurrent write (create or PUT) inserted the same composite key
|
|
// between our existence check and this save. Converge on the caller's intent.
|
|
_context.Entry(addition).State = EntityState.Detached;
|
|
var winner = await _context.UserPermissionOverrides
|
|
.SingleAsync(
|
|
permission => permission.UserId == userId
|
|
&& permission.PermissionKey == permissionKey,
|
|
cancellationToken);
|
|
winner.State = state;
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
}
|
|
|
|
public async Task ClearOverridesAsync(string userId, CancellationToken cancellationToken)
|
|
{
|
|
await _context.UserPermissionOverrides
|
|
.Where(item => item.UserId == userId)
|
|
.ExecuteDeleteAsync(cancellationToken);
|
|
}
|
|
}
|