diff --git a/src/Altinn.AccessManagement.Core/Models/AuthorizationRequestResource.cs b/src/Altinn.AccessManagement.Core/Models/AuthorizationRequestResource.cs index 480ae31e3..05362aba6 100644 --- a/src/Altinn.AccessManagement.Core/Models/AuthorizationRequestResource.cs +++ b/src/Altinn.AccessManagement.Core/Models/AuthorizationRequestResource.cs @@ -30,4 +30,4 @@ public class AuthorizationRequestResource /// public Dictionary Metadata { get; set; } } -} +} \ No newline at end of file diff --git a/src/Altinn.AccessManagement.Core/Models/AuthorizedParty/AuthorizedParty.cs b/src/Altinn.AccessManagement.Core/Models/AuthorizedParty/AuthorizedParty.cs index 49cc479d8..5b6a60ed7 100644 --- a/src/Altinn.AccessManagement.Core/Models/AuthorizedParty/AuthorizedParty.cs +++ b/src/Altinn.AccessManagement.Core/Models/AuthorizedParty/AuthorizedParty.cs @@ -129,6 +129,11 @@ public AuthorizedParty(SblAuthorizedParty sblAuthorizedParty, bool includeSubuni /// public List Subunits { get; set; } = []; + /// + /// Gets or sets a collection of all Authorized Instances + /// + public List AuthorizedInstances { get; set; } = []; + /// /// Enriches this authorized party and any subunits with the list of authorized resources /// @@ -158,4 +163,20 @@ private static string MapAppIdToResourceId(string altinnAppId) return altinnAppId; } + + /// + /// Composite Key instances + /// + public class AuthorizedResource + { + /// + /// Resource ID + /// + public string ResourceId { get; set; } + + /// + /// Instance ID + /// + public string InstanceId { get; set; } + } } \ No newline at end of file diff --git a/src/Altinn.AccessManagement.Core/Repositories/Interfaces/IDelegationMetadataRepository.cs b/src/Altinn.AccessManagement.Core/Repositories/Interfaces/IDelegationMetadataRepository.cs index 7b8d809cd..1b45ee397 100644 --- a/src/Altinn.AccessManagement.Core/Repositories/Interfaces/IDelegationMetadataRepository.cs +++ b/src/Altinn.AccessManagement.Core/Repositories/Interfaces/IDelegationMetadataRepository.cs @@ -29,6 +29,14 @@ public interface IDelegationMetadataRepository /// All the last InstanceDelegationChange records stored in the database corresponding to the request Task> GetAllLatestInstanceDelegationChanges(InstanceDelegationSource source, string resourceID, string instanceID, CancellationToken cancellationToken = default); + /// + /// Returns all received instance delegations from db + /// + /// party uuid that received the delegation + /// The + /// + Task> GetAllCurrentReceivedInstanceDelegations(List toUuid, CancellationToken cancellationToken = default); + /// /// Returns the last change from db to fetch the current policy version and path to policy file /// diff --git a/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs b/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs index 15c9fcfbf..4ef877521 100644 --- a/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs +++ b/src/Altinn.AccessManagement.Core/Services/AppsInstanceDelegationService.cs @@ -101,7 +101,7 @@ private static bool InstanceRightComparesEqualToDelegableRight(Right right, Righ private static bool ValidateAndGetSignificantResourcePartsFromResource(IEnumerable input, out List resource, string resourceTag) { resource = new List(); - + if (input == null || !input.Any()) { return false; @@ -307,7 +307,7 @@ public async Task> Delegate(AppsInstanceD }; List rightsAppCantDelegate = new List(); UrnJsonTypeValue instanceId = KeyValueUrn.CreateUnchecked($"{AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute}:{request.InstanceId}", AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute.Length + 1); - + foreach (RightInternal rightToDelegate in request.Rights) { if (CheckIfInstanceIsDelegable(delegableRights, rightToDelegate)) @@ -418,7 +418,7 @@ private static List RemoveInstanceIdFromResource { foreach (AppsInstanceDelegationResponse item in input) { - RemoveInstanceIdFromResourceForResponse(item); + RemoveInstanceIdFromResourceForResponse(item); } return input; diff --git a/src/Altinn.AccessManagement.Core/Services/AuthorizedPartiesService.cs b/src/Altinn.AccessManagement.Core/Services/AuthorizedPartiesService.cs index 726261015..23154b917 100644 --- a/src/Altinn.AccessManagement.Core/Services/AuthorizedPartiesService.cs +++ b/src/Altinn.AccessManagement.Core/Services/AuthorizedPartiesService.cs @@ -164,10 +164,35 @@ public async Task> GetAuthorizedPartiesForEnterpriseUserUu return await Task.FromResult(new List()); } + private async Task> GetInstanceDelegations(int subjectUserId, List subjectPartyIds, CancellationToken cancellationToken) + { + var parties = new List(); + if (subjectPartyIds?.Count > 0) + { + parties.AddRange(await _contextRetrievalService.GetPartiesAsync(subjectPartyIds, false, cancellationToken) ?? []); + } + + if (subjectUserId != 0) + { + var userProfile = await _profile.GetUser(new() { UserId = subjectUserId }, cancellationToken); + if (userProfile != null) + { + parties.Add(userProfile.Party); + } + } + + if (parties.Count > 0) + { + return await _delegations.GetAllCurrentReceivedInstanceDelegations(parties.Select(p => (Guid)p.PartyUuid).ToList(), cancellationToken); + } + + return []; + } + private async Task> BuildAuthorizedParties(int subjectUserId, List subjectPartyIds, bool includeAltinn2AuthorizedParties, bool includeResourcesThroughRoles, CancellationToken cancellationToken) { - List result = new(); - List a3AuthParties = new(); + List result = []; + List a3AuthParties = []; SortedDictionary authorizedPartyDict = []; if ((includeAltinn2AuthorizedParties || includeResourcesThroughRoles) && subjectUserId != 0) @@ -285,6 +310,36 @@ private async Task> BuildAuthorizedParties(int subjectUser authorizedParty.EnrichWithResourceAccess(delegation.ResourceId); } + var instanceDelegations = await GetInstanceDelegations(subjectUserId, subjectPartyIds, cancellationToken); + var instanceParties = await _contextRetrievalService.GetPartiesByUuids(instanceDelegations.Select(i => i.FromUuid), false, cancellationToken); + foreach (var delegation in instanceDelegations) + { + if (!instanceParties.TryGetValue(delegation.FromUuid.ToString(), out var instanceParty)) + { + throw new UnreachableException($"Get AuthorizedParties failed to lookup party with uuid {delegation.FromUuid} while building instance delegations list"); + } + + // Found existing party. Need to just append instances + if (!authorizedPartyDict.TryGetValue(instanceParty.PartyId, out var authorizedParty)) + { + authorizedParty = new AuthorizedParty(instanceParty); + authorizedPartyDict.Add(authorizedParty.PartyId, authorizedParty); + a3AuthParties.Add(authorizedParty); + } + + // Ensure that we dont't add duplicates + if (authorizedParty.AuthorizedInstances.Exists(instance => instance.InstanceId == delegation.InstanceId && instance.ResourceId == delegation.ResourceId)) + { + continue; + } + + authorizedParty.AuthorizedInstances.Add(new() + { + InstanceId = delegation.InstanceId, + ResourceId = delegation.ResourceId, + }); + } + result.AddRange(a3AuthParties); return result; } @@ -305,4 +360,4 @@ private async Task EnrichPartyWithAuthorizedResourcesThroughRoles(AuthorizedPart } } } -} +} \ No newline at end of file diff --git a/src/Altinn.AccessManagement.Integration/Clients/AltinnRolesClient.cs b/src/Altinn.AccessManagement.Integration/Clients/AltinnRolesClient.cs index 35d349234..cf21e04e4 100644 --- a/src/Altinn.AccessManagement.Integration/Clients/AltinnRolesClient.cs +++ b/src/Altinn.AccessManagement.Integration/Clients/AltinnRolesClient.cs @@ -108,4 +108,4 @@ public async Task> GetAuthorizedPartiesWithRoles(int userI throw; } } -} +} \ No newline at end of file diff --git a/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepo.cs b/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepo.cs index afc015688..58d8c079b 100644 --- a/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepo.cs +++ b/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepo.cs @@ -42,7 +42,7 @@ public DelegationMetadataRepo(NpgsqlDataSource conn) public async Task> GetAllAppDelegationChanges(string altinnAppId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, CancellationToken cancellationToken = default) { using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client); - + if (coveredByUserId == null && coveredByPartyId == null) { activity?.StopWithError(new ArgumentException($"Both params: {nameof(coveredByUserId)}, {nameof(coveredByPartyId)} cannot be null.")); @@ -60,7 +60,7 @@ FROM delegation.delegationChanges AND coveredByPartyId = @coveredByPartyId "; } - + if (coveredByUserId != null) { query = /*strpsql*/@$" @@ -335,6 +335,68 @@ public async Task InsertDelegation(ResourceAttributeMatchType return await InsertResourceRegistryDelegation(delegationChange, cancellationToken); } + /// + /// Fetches all instance delegated to given param + /// + /// list of parties that has received an instance delegation + /// cancellation token + /// + public async Task> GetAllCurrentReceivedInstanceDelegations(List toUuid, CancellationToken cancellationToken = default) + { + using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client); + + var query = /* strpsql */ @" + WITH latestChanges AS ( + SELECT + MAX(instancedelegationchangeid) as latestId + FROM + delegation.instancedelegationchanges + WHERE + touuid = ANY(@toUuid) + GROUP BY + touuid, + fromuuid, + resourceid, + instanceid + ) + SELECT + instancedelegationchangeid, + delegationchangetype, + instanceDelegationMode, + resourceid, + instanceid, + fromuuid, + fromtype, + touuid, + totype, + performedby, + performedbytype, + blobstoragepolicypath, + blobstorageversionid, + created + FROM + delegation.instancedelegationchanges + INNER JOIN latestChanges + ON instancedelegationchangeid = latestChanges.latestId + WHERE + delegationchangetype != 'revoke_last' + "; + + try + { + await using var cmd = _conn.CreateCommand(query); + cmd.Parameters.AddWithValue("toUuid", NpgsqlDbType.Array | NpgsqlDbType.Uuid, toUuid); + return await cmd.ExecuteEnumerableAsync(cancellationToken) + .SelectAwait(GetInstanceDelegationChange) + .ToListAsync(cancellationToken); + } + catch (Exception ex) + { + activity?.StopWithError(ex); + throw; + } + } + /// public async Task GetLastInstanceDelegationChange(InstanceDelegationChangeRequest request, CancellationToken cancellationToken = default) { @@ -515,7 +577,7 @@ LatestChanges lc return await cmd.ExecuteEnumerableAsync(cancellationToken) .SelectAwait(GetInstanceDelegationChange) - .ToListAsync(cancellationToken); + .ToListAsync(cancellationToken); } catch (Exception ex) { @@ -523,7 +585,7 @@ LatestChanges lc throw; } } - + private static async ValueTask GetInstanceDelegationChange(NpgsqlDataReader reader) { using var activity = TelemetryConfig.ActivitySource.StartActivity(); @@ -682,7 +744,7 @@ FROM insertAction AS ins } } - private async Task GetCurrentResourceRegistryDelegation(string resourceId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toUuidType, CancellationToken cancellationToken = default) + private async Task GetCurrentResourceRegistryDelegation(string resourceId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toUuidType, CancellationToken cancellationToken = default) { using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client); @@ -754,7 +816,7 @@ ORDER BY resourceRegistryDelegationChangeId DESC LIMIT 1 cmd.Parameters.AddWithNullableValue("coveredByUserId", NpgsqlDbType.Integer, coveredByUserId); cmd.Parameters.AddWithNullableValue(ToUuid, NpgsqlDbType.Uuid, toUuid); cmd.Parameters.AddWithValue(ToType, toUuidType); - + await using var reader = await cmd.ExecuteReaderAsync(cancellationToken); if (await reader.ReadAsync(cancellationToken)) { @@ -980,7 +1042,7 @@ FROM delegation.ResourceRegistryDelegationChanges AS rrdc INNER JOIN res ON rrdc.resourceId_fk = res.resourceid WHERE coveredByPartyId = ANY (@coveredByPartyIds) "; - + if (offeredByPartyIds != null && offeredByPartyIds.Count > 0) { query += /*strpsql*/@" @@ -1020,7 +1082,7 @@ FROM delegation.ResourceRegistryDelegationChanges AS rr public async Task> GetReceivedResourceRegistryDelegationsForCoveredByUser(int coveredByUserId, List offeredByPartyIds, List resourceRegistryIds = null, List resourceTypes = null, CancellationToken cancellationToken = default) { using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client); - + if (coveredByUserId < 1) { throw new ArgumentException("CoveredByUserId is required"); @@ -1118,7 +1180,7 @@ accessmanagement.Resource AS R AND offeredByPartyId = @offeredByPartyId"; } - if (coveredByPartyId > 0) + if (coveredByPartyId > 0) { query += /*strpsql*/@" AND coveredByPartyId = @coveredByPartyId"; @@ -1271,7 +1333,7 @@ public async Task> GetAllDelegationChangesForAuthorizedPa if (coveredByUserIds == null && coveredByPartyIds == null) { - return new List(); + return []; } const string query = /*strpsql*/@" @@ -1427,4 +1489,4 @@ private static async ValueTask GetResourceRegistryDelegationCh } } } -} +} \ No newline at end of file diff --git a/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepository.cs b/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepository.cs index c7d5c403c..9d9c188d0 100644 --- a/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepository.cs +++ b/src/Altinn.AccessManagement.Persistence/DelegationMetadataRepository.cs @@ -755,4 +755,10 @@ public Task> GetAllLatestInstanceDelegationChange { throw new NotImplementedException(); } + + /// + public Task> GetAllCurrentReceivedInstanceDelegations(List toUuid, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/src/Altinn.AccessManagement/AccessManagementHost.cs b/src/Altinn.AccessManagement/AccessManagementHost.cs index 2487b2724..f8fcca562 100644 --- a/src/Altinn.AccessManagement/AccessManagementHost.cs +++ b/src/Altinn.AccessManagement/AccessManagementHost.cs @@ -104,7 +104,7 @@ private static void ConfigureOpenAPI(this WebApplicationBuilder builder) while (t != null); chain.Reverse(); return string.Join(".", chain); - }; + }; }); builder.Services.AddUrnSwaggerSupport(); diff --git a/src/Altinn.AccessManagement/Controllers/PolicyInformationPointController.cs b/src/Altinn.AccessManagement/Controllers/PolicyInformationPointController.cs index 0a794522d..bef8ec58a 100644 --- a/src/Altinn.AccessManagement/Controllers/PolicyInformationPointController.cs +++ b/src/Altinn.AccessManagement/Controllers/PolicyInformationPointController.cs @@ -53,4 +53,4 @@ public async Task>> GetAllDelegation return _mapper.Map>(response.DelegationChanges); } } -} +} \ No newline at end of file diff --git a/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs b/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs index 56c5aea0a..376856dc4 100644 --- a/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs +++ b/src/Altinn.AccessManagement/Controllers/ResourceOwnerAPI/AppsInstanceDelegationController.cs @@ -119,8 +119,8 @@ public async Task Delegation([FromBody] AppsInstanceDelegationRequ return Ok(_mapper.Map(serviceResult.Value)); } - return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map(serviceResult.Value)); - } + return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map(serviceResult.Value)); + } /// /// Gets app instance delegation diff --git a/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs b/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs index 6dc3498b5..1bd729736 100644 --- a/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs +++ b/src/Altinn.AccessManagement/Mappers/AccessManagementMapper.cs @@ -80,6 +80,7 @@ public AccessManagementMapper() CreateMap(); CreateMap(); + CreateMap(); CreateMap(); CreateMap() .ForMember(dest => dest.From, act => act.MapFrom(src => src.From.Value)) diff --git a/src/Altinn.AccessManagement/Models/AuthorizedPartyExternal.cs b/src/Altinn.AccessManagement/Models/AuthorizedPartyExternal.cs index ed0fd9bfc..c7a7eca63 100644 --- a/src/Altinn.AccessManagement/Models/AuthorizedPartyExternal.cs +++ b/src/Altinn.AccessManagement/Models/AuthorizedPartyExternal.cs @@ -63,8 +63,29 @@ public class AuthorizedPartyExternal /// public List AuthorizedRoles { get; set; } = []; + /// + /// Gets or sets a collection of all Authorized Instances + /// + public List AuthorizedInstances { get; set; } = []; + /// /// Gets or sets a set of subunits of this party, which the authorized subject also has some access to. /// public List Subunits { get; set; } = []; + + /// + /// Composite Key instances + /// + public class AuthorizedResource + { + /// + /// Resource ID + /// + public string ResourceId { get; set; } + + /// + /// Instance ID + /// + public string InstanceId { get; set; } + } } \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Controllers/V2AuthorizedPartiesControllerTest.cs b/test/Altinn.AccessManagement.Tests/Controllers/V2AuthorizedPartiesControllerTest.cs new file mode 100644 index 000000000..b69c6fed6 --- /dev/null +++ b/test/Altinn.AccessManagement.Tests/Controllers/V2AuthorizedPartiesControllerTest.cs @@ -0,0 +1,78 @@ +using System.Net.Http.Json; +using Altinn.AccessManagement.Controllers; +using Altinn.AccessManagement.Core.Helpers.Extensions; +using Altinn.AccessManagement.Core.Models; +using Altinn.AccessManagement.Tests.Fixtures; +using Altinn.AccessManagement.Tests.Scenarios; +using Altinn.AccessManagement.Tests.Seeds; + +namespace Altinn.AccessManagement.Tests.Controllers; + +/// +/// +/// +public class V2AuthorizedPartiesControllerTest(WebApplicationFixture fixture) : IClassFixture +{ + private WebApplicationFixture Fixture { get; } = fixture; + + private static Action WithAssertDbContainsDelegations(IParty from, IAccessManagementResource resource) => test => + { + test.ApiAssertions.Add(async host => + { + var delegations = await host.Repository.DelegationMetadataRepository.GetAllCurrentAppDelegationChanges(from.Party.PartyId.SingleToList(), resource.DbResource.ResourceRegistryId.SingleToList()); + Assert.True( + delegations.Count > 0, + $"Couldn't find any delegations from {from.Party.PartyId} to app {resource.DbResource.ResourceRegistryId}"); + }); + }; + + private static Action WithAssertResponseContainsInstance(string resourceId, string instanceId) => test => + { + test.ResponseAssertions.Add(async response => + { + var delegations = await response.Content.ReadFromJsonAsync>(); + var result = delegations.Any(delegation => delegation.AuthorizedInstances.Any(instance => instance.ResourceId == resourceId && instance.InstanceId == instanceId)); + Assert.True(result, $"Response don't contains instance delegations with resource Id {resourceId} and instance id {instanceId}"); + }); + }; + + /// + /// Seeds for + /// + /// Acceptance Criteria + /// modifiers for + public class GetAuthorizedParties(string acceptanceCriteria, params Action[] actions) : AcceptanceCriteriaComposer( + acceptanceCriteria, + actions, + WithRequestRoute("accessmanagement", "api", "v1", "authorizedparties"), + WithRequestVerb(HttpMethod.Get)) + { + /// + /// Seeds + /// + public static TheoryData Seeds() => [ + new( + /* Acceptance Critieria */ @" + GIVEN that organization Voss has shared an instance with DAGL Olav for Orstad Accounting + WHEN DAGL Olav for Orstad Accounting requests authorized parties + THEN Organization should be in the list of authorized parties + AND the instance and resource id should be included in list containing instances", + + WithScenarios( + DelegationScenarios.Defaults, + DelegationScenarios.WithInstanceDelegation(OrganizationSeeds.VossAccounting.Defaults, PersonSeeds.Paula.Defaults, ResourceSeeds.ChalkboardResource.Defaults, "1337"), + TokenScenario.PersonToken(PersonSeeds.Olav.Defaults)), + + WithAssertResponseContainsInstance(ResourceSeeds.ChalkboardResource.Identifier, "1337"), + WithAssertResponseStatusCodeSuccessful), + ]; + } + + /// + /// + /// + /// acceptance test + [Theory] + [MemberData(nameof(GetAuthorizedParties.Seeds), MemberType = typeof(GetAuthorizedParties))] + public async Task GET_AuthorizedParties(GetAuthorizedParties acceptanceCriteria) => await acceptanceCriteria.Test(Fixture); +} \ No newline at end of file diff --git a/test/Altinn.AccessManagement.Tests/Mocks/DelegationMetadataRepositoryMock.cs b/test/Altinn.AccessManagement.Tests/Mocks/DelegationMetadataRepositoryMock.cs index 9fb981eb5..063c9d498 100644 --- a/test/Altinn.AccessManagement.Tests/Mocks/DelegationMetadataRepositoryMock.cs +++ b/test/Altinn.AccessManagement.Tests/Mocks/DelegationMetadataRepositoryMock.cs @@ -87,7 +87,7 @@ public Task GetLastInstanceDelegationChange(InstanceDe switch (request.Instance) { case "00000000-0000-0000-0000-000000000001": - + return Task.FromResult(new InstanceDelegationChange { FromUuidType = request.FromType, @@ -158,7 +158,7 @@ private static string GetDelegationPolicyPathFromInstanceRule(InstanceDelegation sb.Append('/'); sb.Append(change.InstanceId.AsFileName(false)); - + sb.Append('/'); sb.Append(change.InstanceDelegationMode); @@ -595,4 +595,9 @@ public Task> GetAllLatestInstanceDelegationChange return Task.FromResult(result); } } + + public Task> GetAllCurrentReceivedInstanceDelegations(List toUuid, CancellationToken cancellationToken = default) + { + return Task.FromResult(new List()); + } } diff --git a/test/Altinn.AccessManagement.Tests/Mocks/PartiesClientMock.cs b/test/Altinn.AccessManagement.Tests/Mocks/PartiesClientMock.cs index f6fd0621d..c16edc81f 100644 --- a/test/Altinn.AccessManagement.Tests/Mocks/PartiesClientMock.cs +++ b/test/Altinn.AccessManagement.Tests/Mocks/PartiesClientMock.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Altinn.AccessManagement.Core.Clients.Interfaces; using Altinn.AccessManagement.Core.Models.SblBridge; +using Altinn.AccessManagement.Tests.Seeds; using Altinn.Platform.Register.Models; namespace Altinn.AccessManagement.Tests.Mocks; @@ -16,14 +17,31 @@ namespace Altinn.AccessManagement.Tests.Mocks; /// public class PartiesClientMock : IPartiesClient { + private Dictionary AdditionalParties { get; set; } = new Dictionary() + { + { PersonSeeds.Paula.PartyId, PersonSeeds.Paula.Party }, + { PersonSeeds.Kasper.PartyId, PersonSeeds.Kasper.Party }, + { PersonSeeds.Olav.PartyId, PersonSeeds.Olav.Party }, + }; + /// public Task> GetPartiesAsync(List partyIds, bool includeSubunits = false, CancellationToken cancellationToken = default) { + var result = new List(); + foreach (var partyId in partyIds) + { + if (AdditionalParties.TryGetValue(partyId, out var party)) + { + result.Add(party); + } + } + List partyList = GetTestDataParties(); List filteredList = (from int partyId in partyIds.Distinct() let party = partyList.Find(p => p.PartyId == partyId) where party != null select party).ToList(); + result.AddRange(filteredList); return Task.FromResult(filteredList); } @@ -48,7 +66,7 @@ public Task LookupPartyBySSNOrOrgNo(PartyLookup partyLookup, Cancellation { List partyList = GetTestDataParties(); Party party = null; - + if (!string.IsNullOrWhiteSpace(partyLookup.OrgNo)) { party = partyList.Find(p => p.Organization?.OrgNumber == partyLookup.OrgNo); diff --git a/test/Altinn.AccessManagement.Tests/Scenarios/DelegationScenarios.cs b/test/Altinn.AccessManagement.Tests/Scenarios/DelegationScenarios.cs index 050658186..eec2dcba2 100644 --- a/test/Altinn.AccessManagement.Tests/Scenarios/DelegationScenarios.cs +++ b/test/Altinn.AccessManagement.Tests/Scenarios/DelegationScenarios.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using System.Linq; using Altinn.AccessManagement.Core.Enums; using Altinn.AccessManagement.Core.Models; @@ -35,6 +36,21 @@ public static void Defaults(MockContext mock) ResourceSeeds.MaskinportenSchema.Defaults, ]); + mock.Parties.AddRange([ + PersonSeeds.Paula.Party, + PersonSeeds.Kasper.Party, + PersonSeeds.Olav.Party, + OrganizationSeeds.Voss.Defaults, + OrganizationSeeds.VossAccounting.Defaults, + OrganizationSeeds.VossConsulting.Defaults, + ]); + + mock.UserProfiles.AddRange([ + PersonSeeds.Paula.Defaults, + PersonSeeds.Kasper.Defaults, + PersonSeeds.Olav.Defaults, + ]); + // Seed databases resources mock.DbSeeds.AddRange([ async postgres => await postgres.ResourceMetadataRepository.InsertAccessManagementResource(ResourceSeeds.AltinnApp.Defaults.DbResource), @@ -132,6 +148,29 @@ public static Scenario WithRevokedDelegationToUser(IParty organization, IUserPro ]); }; + public static Scenario WithInstanceDelegation(IParty from, IParty to, IAccessManagementResource resource, string instanceId) => mock => + { + var insert = new InstanceDelegationChange() + { + DelegationChangeType = DelegationChangeType.Grant, + BlobStoragePolicyPath = "https://blob.storage.no", + BlobStorageVersionId = "v1", + ResourceId = resource.Resource.Identifier, + InstanceId = instanceId, + FromUuid = (Guid)from.Party.PartyUuid, + FromUuidType = string.IsNullOrEmpty(from.Party.SSN) ? Enums.UuidType.Organization : Enums.UuidType.Person, + ToUuid = (Guid)to.Party.PartyUuid, + ToUuidType = string.IsNullOrEmpty(to.Party.SSN) ? Enums.UuidType.Organization : Enums.UuidType.Person, + InstanceDelegationMode = InstanceDelegationMode.Normal, + PerformedBy = from.Party.PartyId.ToString(), + PerformedByType = Enums.UuidType.Person + }; + + mock.DbSeeds.AddRange([ + async postgres => await postgres.DelegationMetadataRepository.InsertInstanceDelegation(insert), + ]); + }; + /// /// Adds mock context and db seeds. for given organization, person and resource /// diff --git a/test/Altinn.AccessManagement.Tests/Seeds/PersonSeeds.cs b/test/Altinn.AccessManagement.Tests/Seeds/PersonSeeds.cs index d22bc5f4e..6c13c74ac 100644 --- a/test/Altinn.AccessManagement.Tests/Seeds/PersonSeeds.cs +++ b/test/Altinn.AccessManagement.Tests/Seeds/PersonSeeds.cs @@ -28,6 +28,7 @@ public class Paula : PersonBase Name = "PAULA RIMSTAD", IsDeleted = false, OnlyHierarchyElementWithNoAccess = false, + PartyUuid = UserUuid, Person = new() { SSN = "02056260016", @@ -78,6 +79,7 @@ public class Olav : PersonBase { PartyTypeName = PartyType.Person, SSN = "27099450067", + PartyUuid = UserUuid, PartyId = PartyId, Name = "ØRJAN RAVNÅS", IsDeleted = false, @@ -133,6 +135,7 @@ public class Kasper : PersonBase PartyTypeName = PartyType.Person, SSN = "07124912037", PartyId = PartyId, + PartyUuid = UserUuid, Name = "KASPER BØRSTAD", IsDeleted = false, OnlyHierarchyElementWithNoAccess = false, diff --git a/test/Altinn.AccessManagement.Tests/Seeds/ResourceSeeds.cs b/test/Altinn.AccessManagement.Tests/Seeds/ResourceSeeds.cs index 9fd87ebaa..a10958475 100644 --- a/test/Altinn.AccessManagement.Tests/Seeds/ResourceSeeds.cs +++ b/test/Altinn.AccessManagement.Tests/Seeds/ResourceSeeds.cs @@ -20,6 +20,26 @@ public class ResourceBase : ServiceResource, IAccessManagementResource }; } + public class ChalkboardResource : ResourceBase + { + public new static readonly ResourceType ResourceType = ResourceType.GenericAccessResource; + + public new static readonly string Identifier = "chalkboard"; + + public static ChalkboardResource Defaults { get; } = new ChalkboardResource(); + + public ChalkboardResource(params Action[] modifiers) + { + base.ResourceType = ResourceType.Systemresource; + base.Identifier = Identifier; + + foreach (var modifer in modifiers) + { + modifer(this); + } + } + } + public class MaskinportenSchema : ResourceBase { public new static readonly string Identifier = "maskinportenschema";