Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using SelfService.Domain.Models;
using SelfService.Infrastructure.Persistence.Queries;

namespace SelfService.Tests.Infrastructure.Queries;

public class TestAadAwsSyncCapabilityQuery
{
[Fact]
[Trait("Category", "InMemoryDatabase")]
public async Task returns_empty_members_for_non_compliant_capabilities()
{
using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5));

await using var databaseFactory = new InMemoryDatabaseFactory();
var dbContext = await databaseFactory.CreateSelfServiceDbContext();

var compliantCapability = A
.Capability.WithId(CapabilityId.CreateFrom("compliant-capability"))
.WithName("compliant-capability")
.WithJsonMetadata(
"""
{
"dfds.cost.centre": "1234",
"dfds.businessCapability": "platform",
"dfds.env": "prod",
"dfds.data.classification": "internal",
"dfds.service.criticality": "high",
"dfds.service.availability": "24x7"
}
"""
)
.Build();

var nonCompliantCapability = A
.Capability.WithId(CapabilityId.CreateFrom("non-compliant-capability"))
.WithName("non-compliant-capability")
.WithJsonMetadata("{}")
.Build();

var compliantMember = A.Member.WithUserId(UserId.Parse("compliant-user")).Build();
var nonCompliantMember = A.Member.WithUserId(UserId.Parse("non-compliant-user")).Build();

var compliantMembership = A
.Membership.WithCapabilityId(compliantCapability.Id)
.WithUserId(compliantMember.Id)
.Build();

var nonCompliantMembership = A
.Membership.WithCapabilityId(nonCompliantCapability.Id)
.WithUserId(nonCompliantMember.Id)
.Build();

await dbContext.Capabilities.AddRangeAsync(
new[] { compliantCapability, nonCompliantCapability },
cancellationTokenSource.Token
);
await dbContext.Members.AddRangeAsync(
new[] { compliantMember, nonCompliantMember },
cancellationTokenSource.Token
);
await dbContext.Memberships.AddRangeAsync(
new[] { compliantMembership, nonCompliantMembership },
cancellationTokenSource.Token
);
await dbContext.SaveChangesAsync(cancellationTokenSource.Token);

var sut = new AadAwsSyncCapabilityQuery(dbContext);
var result = (await sut.GetCapabilities()).ToDictionary(x => x.Id);

Assert.Single(result[compliantCapability.Id].Members);
Assert.Equal(compliantMember.Id.ToString(), result[compliantCapability.Id].Members[0].UserId);

Assert.Empty(result[nonCompliantCapability.Id].Members);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using SelfService.Domain.Models;
using SelfService.Domain.Services;
using SelfService.Infrastructure.Api.System;

namespace SelfService.Infrastructure.Persistence.Queries;
Expand All @@ -24,29 +25,32 @@ public async Task<IEnumerable<CapabilityDto>> GetCapabilities()
return from capability in allCapabilities
let memberships = allMemberships[capability.Id]
let awsAccounts = allAwsAccounts[capability.Id]
let isTagCompliant = TagComplianceEvaluator.Evaluate(capability.JsonMetadata).IsCompliant
select new CapabilityDto
{
Id = capability.Id,
Name = capability.Name,
RootId = capability.Id,
Description = capability.Description,
JsonMetadata = capability.JsonMetadata,
Members = memberships
.Select<Membership, MemberDto>(member => new MemberDto
{
// Membership.UserId is the member's id; for service principals this is the
// Azure object id, so resolve the (synthetic) email from the Member record.
// Fall back to UserId for memberships without a matching Member row.
Email = emailByUserId.GetValueOrDefault(member.UserId, member.UserId),
// UserId is the authoritative identifier (the UPN for regular users);
// aad-aws-sync uses it to resolve the user in Azure AD directly.
UserId = member.UserId.ToString(),
// User has access to third-party services if their role is Owner or Contributor
HasAccessToThirdParty =
rolesByCapabilityAndUserId.TryGetValue((capability.Id, member.UserId), out var role)
&& (role == "Owner" || role == "Contributor"),
})
.ToArray(),
Members = isTagCompliant
? memberships
.Select<Membership, MemberDto>(member => new MemberDto
{
// Membership.UserId is the member's id; for service principals this is the
// Azure object id, so resolve the (synthetic) email from the Member record.
// Fall back to UserId for memberships without a matching Member row.
Email = emailByUserId.GetValueOrDefault(member.UserId, member.UserId),
// UserId is the authoritative identifier (the UPN for regular users);
// aad-aws-sync uses it to resolve the user in Azure AD directly.
UserId = member.UserId.ToString(),
// User has access to third-party services if their role is Owner or Contributor
HasAccessToThirdParty =
rolesByCapabilityAndUserId.TryGetValue((capability.Id, member.UserId), out var role)
&& (role == "Owner" || role == "Contributor"),
})
.ToArray()
: Array.Empty<MemberDto>(),
Contexts = awsAccounts
.Select<AwsAccount, ContextDto>(context => new ContextDto
{
Expand Down
Loading