122 lines
4.3 KiB
C#
122 lines
4.3 KiB
C#
using FastEndpoints;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Security.Claims;
|
|
using Socialize.Api.Data;
|
|
using Socialize.Api.Modules.Identity.Data;
|
|
using Socialize.Api.Infrastructure.Security;
|
|
using Socialize.Api.Modules.Workspaces.Data;
|
|
|
|
namespace Socialize.Api.Modules.Workspaces.Handlers;
|
|
|
|
public record WorkspaceMemberDto(
|
|
Guid Id,
|
|
string DisplayName,
|
|
string Email,
|
|
string? PortraitUrl,
|
|
string RelationshipCategory,
|
|
IReadOnlyCollection<string> Roles);
|
|
|
|
public class GetWorkspaceMembersHandler(
|
|
AppDbContext dbContext,
|
|
AccessScopeService accessScopeService)
|
|
: EndpointWithoutRequest<IReadOnlyCollection<WorkspaceMemberDto>>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
Get("/api/workspaces/{workspaceId:guid}/members");
|
|
Options(o => o.WithTags("Workspaces"));
|
|
}
|
|
|
|
public override async Task HandleAsync(CancellationToken ct)
|
|
{
|
|
Guid workspaceId = Route<Guid>("workspaceId");
|
|
|
|
if (!await accessScopeService.CanManageWorkspaceAsync(User, workspaceId, ct))
|
|
{
|
|
await SendForbiddenAsync(ct);
|
|
return;
|
|
}
|
|
|
|
Workspace? workspace = await dbContext.Workspaces
|
|
.SingleOrDefaultAsync(candidate => candidate.Id == workspaceId, ct);
|
|
if (workspace is null)
|
|
{
|
|
await SendNotFoundAsync(ct);
|
|
return;
|
|
}
|
|
|
|
string workspaceClaimValue = workspaceId.ToString();
|
|
|
|
var users = await dbContext.Users
|
|
.Where(candidate =>
|
|
dbContext.UserClaims.Any(claim =>
|
|
claim.UserId == candidate.Id &&
|
|
claim.ClaimType == KnownClaims.WorkspaceScope &&
|
|
claim.ClaimValue == workspaceClaimValue) ||
|
|
dbContext.OrganizationMemberships.Any(membership =>
|
|
membership.UserId == candidate.Id &&
|
|
membership.OrganizationId == workspace.OrganizationId) ||
|
|
candidate.Id == workspace.OwnerUserId)
|
|
.OrderBy(candidate => candidate.Lastname)
|
|
.ThenBy(candidate => candidate.Firstname)
|
|
.ThenBy(candidate => candidate.Email)
|
|
.ToListAsync(ct);
|
|
|
|
var userIds = users
|
|
.Select(candidate => candidate.Id)
|
|
.ToList();
|
|
|
|
Dictionary<Guid, IReadOnlyCollection<string>> rolesByUserId = await dbContext.UserRoles
|
|
.Where(candidate => userIds.Contains(candidate.UserId))
|
|
.Join(
|
|
dbContext.Roles,
|
|
userRole => userRole.RoleId,
|
|
role => role.Id,
|
|
(userRole, role) => new { userRole.UserId, role.Name })
|
|
.GroupBy(candidate => candidate.UserId)
|
|
.ToDictionaryAsync(
|
|
group => group.Key,
|
|
group => (IReadOnlyCollection<string>)group
|
|
.Select(candidate => candidate.Name)
|
|
.Where(name => !string.IsNullOrWhiteSpace(name))
|
|
.Cast<string>()
|
|
.OrderBy(name => name)
|
|
.ToArray(),
|
|
ct);
|
|
|
|
HashSet<Guid> organizationMemberUserIds = await dbContext.OrganizationMemberships
|
|
.Where(membership => membership.OrganizationId == workspace.OrganizationId)
|
|
.Select(membership => membership.UserId)
|
|
.ToHashSetAsync(ct);
|
|
organizationMemberUserIds.Add(workspace.OwnerUserId);
|
|
|
|
var members = users
|
|
.Select(candidate => new WorkspaceMemberDto(
|
|
candidate.Id,
|
|
BuildDisplayName(candidate),
|
|
candidate.Email ?? string.Empty,
|
|
candidate.PortraitUrl,
|
|
organizationMemberUserIds.Contains(candidate.Id) ? "Organization Member" : "External Collaborator",
|
|
rolesByUserId.GetValueOrDefault(candidate.Id) ?? Array.Empty<string>()))
|
|
.ToList();
|
|
|
|
await SendOkAsync(members, ct);
|
|
}
|
|
|
|
private static string BuildDisplayName(User user)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(user.Alias))
|
|
{
|
|
return user.Alias;
|
|
}
|
|
|
|
string fullName = $"{user.Firstname} {user.Lastname}".Trim();
|
|
if (!string.IsNullOrWhiteSpace(fullName))
|
|
{
|
|
return fullName;
|
|
}
|
|
|
|
return user.Email ?? user.UserName ?? user.Id.ToString();
|
|
}
|
|
}
|