Skip to content

Commit

Permalink
extend authorized parties with instance delegations
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasisnes committed Oct 23, 2024
1 parent b938088 commit 25f21d6
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public AuthorizedParty()
/// </summary>
/// <param name="party">Party model from registry</param>
/// <param name="includeSubunits">Whether model should also build list of subunits if any exists</param>
public AuthorizedParty(Party party, bool includeSubunits = true)
/// <param name="isInstanceDelegation"></param>
public AuthorizedParty(Party party, bool includeSubunits = true, bool isInstanceDelegation = true)
{
PartyId = party.PartyId;
PartyUuid = party.PartyUuid.Value;
Expand Down Expand Up @@ -114,6 +115,11 @@ public AuthorizedParty(SblAuthorizedParty sblAuthorizedParty, bool includeSubuni
/// </summary>
public bool OnlyHierarchyElementWithNoAccess { get; set; }

/// <summary>
/// Gets or sets a collection of all Authorized Instances
/// </summary>
public List<string> AuthorizedInstances { get; set; } = [];

/// <summary>
/// Gets or sets a collection of all resource identifier the authorized subject has some access to on behalf of this party
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public interface IDelegationMetadataRepository
/// <returns>The complete DelegationChange record stored in the database</returns>
Task<DelegationChange> InsertDelegation(ResourceAttributeMatchType resourceMatchType, DelegationChange delegationChange, CancellationToken cancellationToken = default);

/// <summary>
/// Returns all received instance delegations from db
/// </summary>
/// <param name="toUuid">party uuid that received the delegation</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/></param>
/// <returns></returns>
Task<List<InstanceDelegationChange>> GetAllCurrentReceviedInstanceDelegations(List<Guid> toUuid, CancellationToken cancellationToken = default);

/// <summary>
/// Returns the last change from db to fetch the current policy version and path to policy file
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private static bool InstanceRightComparesEqualToDelegableRight(Right right, Righ
private static bool ValidateAndGetSignificantResourcePartsFromResource(IEnumerable<UrnJsonTypeValue> input, out List<UrnJsonTypeValue> resource, string resourceTag)
{
resource = new List<UrnJsonTypeValue>();

if (input == null || !input.Any())
{
return false;
Expand Down Expand Up @@ -306,7 +306,7 @@ public async Task<Result<AppsInstanceDelegationResponse>> Delegate(AppsInstanceD
};
List<RightInternal> rightsAppCantDelegate = new List<RightInternal>();
UrnJsonTypeValue instanceId = KeyValueUrn.CreateUnchecked($"{AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute}:{request.InstanceId}", AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute.Length + 1);

foreach (RightInternal rightToDelegate in request.Rights)
{
if (CheckIfInstanceIsDelegable(delegableRights, rightToDelegate))
Expand Down Expand Up @@ -336,7 +336,7 @@ public async Task<Result<AppsInstanceDelegationResponse>> Delegate(AppsInstanceD
List<InstanceRightDelegationResult> rights = await DelegateRights(rulesToDelegate, rightsAppCantDelegate, cancellationToken);

result.Rights = rights;

return result;
}

Expand All @@ -363,7 +363,7 @@ public Task<Result<bool>> Get(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}

/// <inheritdoc/>
public Task<Result<bool>> Revoke(CancellationToken cancellationToken = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,18 @@ public async Task<List<AuthorizedParty>> GetAuthorizedPartiesForEnterpriseUserUu
return await Task.FromResult(new List<AuthorizedParty>());
}

private async Task<List<InstanceDelegationChange>> GetInstanceDelegations(int subjectUserId, List<int> subjectPartyIds, CancellationToken cancellationToken)
{
var userId = subjectUserId != 0 ? subjectUserId.SingleToList() : [];
userId.AddRange(subjectPartyIds);
var parties = await _contextRetrievalService.GetPartiesAsync(userId, false, cancellationToken);
return await _delegations.GetAllCurrentReceviedInstanceDelegations(parties.Select(p => (Guid)p.PartyUuid).ToList(), cancellationToken);
}

private async Task<List<AuthorizedParty>> BuildAuthorizedParties(int subjectUserId, List<int> subjectPartyIds, bool includeAltinn2AuthorizedParties, bool includeResourcesThroughRoles, CancellationToken cancellationToken)
{
List<AuthorizedParty> result = new();
List<AuthorizedParty> a3AuthParties = new();
List<AuthorizedParty> result = [];
List<AuthorizedParty> a3AuthParties = [];
SortedDictionary<int, AuthorizedParty> authorizedPartyDict = [];

if ((includeAltinn2AuthorizedParties || includeResourcesThroughRoles) && subjectUserId != 0)
Expand Down Expand Up @@ -285,6 +293,26 @@ private async Task<List<AuthorizedParty>> 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");
}

if (authorizedPartyDict.TryGetValue(instanceParty.PartyId, out var authorizedParty))
{
continue;
}

authorizedParty = new AuthorizedParty(instanceParty);
authorizedParty.AuthorizedInstances.Add(delegation.Instance);
authorizedPartyDict.Add(authorizedParty.PartyId, authorizedParty);
a3AuthParties.Add(authorizedParty);
}

result.AddRange(a3AuthParties);
return result;
}
Expand All @@ -305,4 +333,4 @@ private async Task EnrichPartyWithAuthorizedResourcesThroughRoles(AuthorizedPart
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,4 @@ public async Task<List<AuthorizedParty>> GetAuthorizedPartiesWithRoles(int userI
throw;
}
}
}
}
95 changes: 86 additions & 9 deletions src/Altinn.AccessManagement.Persistence/DelegationMetadataRepo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public DelegationMetadataRepo(NpgsqlDataSource conn)
public async Task<List<DelegationChange>> 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."));
Expand All @@ -58,7 +58,7 @@ FROM delegation.delegationChanges
AND coveredByPartyId = @coveredByPartyId
";
}

if (coveredByUserId != null)
{
query = /*strpsql*/@$"
Expand Down Expand Up @@ -333,6 +333,55 @@ public async Task<DelegationChange> InsertDelegation(ResourceAttributeMatchType
return await InsertResourceRegistryDelegation(delegationChange, cancellationToken);
}

/// <summary>
/// Fetches all instance delegated to given param
/// </summary>
/// <param name="toUuid"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<List<InstanceDelegationChange>> GetAllCurrentReceviedInstanceDelegations(List<Guid> toUuid, CancellationToken cancellationToken = default)
{
using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client);

var query = /*strpsql*/ @"
SELECT
instancedelegationchangeid,
delegationchangetype,
instanceDelegationMode,
resourceid,
instanceid,
fromuuid,
fromtype,
touuid,
totype,
performedby,
performedbytype,
blobstoragepolicypath,
blobstorageversionid,
created
FROM
delegation.instancedelegationchanges
WHERE
touuid = ANY(@toUuid)
GROUP BY
resourceid, touuid, fromuuid;
";

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;
}
}

/// <inheritdoc />
public async Task<InstanceDelegationChange> GetLastInstanceDelegationChange(InstanceDelegationChangeRequest request, CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -618,7 +667,7 @@ FROM insertAction AS ins
}
}

private async Task<DelegationChange> GetCurrentResourceRegistryDelegation(string resourceId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toUuidType, CancellationToken cancellationToken = default)
private async Task<DelegationChange> GetCurrentResourceRegistryDelegation(string resourceId, int offeredByPartyId, int? coveredByPartyId, int? coveredByUserId, Guid? toUuid, UuidType toUuidType, CancellationToken cancellationToken = default)
{
using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client);

Expand Down Expand Up @@ -690,7 +739,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))
{
Expand Down Expand Up @@ -916,7 +965,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*/@"
Expand Down Expand Up @@ -956,7 +1005,7 @@ FROM delegation.ResourceRegistryDelegationChanges AS rr
public async Task<List<DelegationChange>> GetReceivedResourceRegistryDelegationsForCoveredByUser(int coveredByUserId, List<int> offeredByPartyIds, List<string> resourceRegistryIds = null, List<ResourceType> resourceTypes = null, CancellationToken cancellationToken = default)
{
using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client);

if (coveredByUserId < 1)
{
throw new ArgumentException("CoveredByUserId is required");
Expand Down Expand Up @@ -1054,7 +1103,7 @@ accessmanagement.Resource AS R
AND offeredByPartyId = @offeredByPartyId";
}

if (coveredByPartyId > 0)
if (coveredByPartyId > 0)
{
query += /*strpsql*/@"
AND coveredByPartyId = @coveredByPartyId";
Expand Down Expand Up @@ -1201,13 +1250,13 @@ FROM delegation.delegationchanges
}

/// <inheritdoc/>
public async Task<List<DelegationChange>> GetAllDelegationChangesForAuthorizedParties(List<int> coveredByUserIds, List<int> coveredByPartyIds, CancellationToken cancellationToken = default)
public async Task<List<DelegationChange>> GetAllDelegationChangesForAuthorizedParties(List<int> coveredByUserIds, List<int> coveredByPartyIds, List<Guid> toPartyUuid, CancellationToken cancellationToken = default)
{
using var activity = TelemetryConfig.ActivitySource.StartActivity(ActivityKind.Client);

if (coveredByUserIds == null && coveredByPartyIds == null)
{
return new List<DelegationChange>();
return [];
}

const string query = /*strpsql*/@"
Expand All @@ -1227,7 +1276,29 @@ SELECT MAX(delegationChangeId) as latestId
FROM delegation.delegationchanges
WHERE coveredByUserId = ANY (@coveredByUserIds) OR coveredByPartyId = ANY (@coveredByPartyIds)
GROUP BY altinnAppId, offeredByPartyId, coveredByUserId, coveredByPartyId
),
latestInstanceChanges AS (
SELECT MAX(instancedelegationchangeid) as latestId
FROM delegation.instancedelegationchanges
WHERE touuid = ANY(@touuid)
GROUP BY resourcid, fromuuid, touuid
)
SELECT
instancedelegationchangeid,
delegationchangetype,
instanceDelegationMode,
resourceid,
instanceid,
fromuuid,
fromtype,
touuid,
totype,
performedby,
performedbytype,
blobstoragepolicypath,
blobstorageversionid,
created,
SELECT
resourceRegistryDelegationChangeId,
null AS delegationChangeId,
Expand Down Expand Up @@ -1279,6 +1350,7 @@ FROM delegation.delegationchanges
try
{
await using var cmd = _conn.CreateCommand(query);
cmd.Parameters.AddWithNullableValue("touuid", NpgsqlDbType.Array | NpgsqlDbType.Uuid, toPartyUuid);
cmd.Parameters.AddWithNullableValue("coveredByUserIds", NpgsqlDbType.Array | NpgsqlDbType.Integer, coveredByUserIds);
cmd.Parameters.AddWithNullableValue("coveredByPartyIds", NpgsqlDbType.Array | NpgsqlDbType.Integer, coveredByPartyIds);

Expand Down Expand Up @@ -1362,5 +1434,10 @@ private static async ValueTask<DelegationChange> GetResourceRegistryDelegationCh
return await new ValueTask<DelegationChange>(Task.FromException<DelegationChange>(ex));
}
}

public Task<List<DelegationChange>> GetAllDelegationChangesForAuthorizedParties(List<int> coveredByUserIds, List<int> coveredByPartyIds, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}
2 changes: 1 addition & 1 deletion src/Altinn.AccessManagement/AccessManagementHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private static void ConfigureOpenAPI(this WebApplicationBuilder builder)
while (t != null);
chain.Reverse();
return string.Join(".", chain);
};
};
});

builder.Services.AddUrnSwaggerSupport();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ public async Task<ActionResult<List<DelegationChangeExternal>>> GetAllDelegation
return _mapper.Map<List<DelegationChangeExternal>>(response.DelegationChanges);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ public async Task<ActionResult> Delegation([FromBody] AppsInstanceDelegationRequ
return Ok(_mapper.Map<AppsInstanceDelegationResponseDto>(serviceResult.Value));
}

return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map<AppsInstanceDelegationResponseDto>(serviceResult.Value));
}
return StatusCode(StatusCodes.Status206PartialContent, _mapper.Map<AppsInstanceDelegationResponseDto>(serviceResult.Value));
}

/*
/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Altinn.AccessManagement/Models/AuthorizedPartyExternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public class AuthorizedPartyExternal
/// </summary>
public List<string> AuthorizedRoles { get; set; } = [];

/// <summary>
/// Gets or sets a collection of all Authorized Instances
/// </summary>
public List<string> AuthorizedInstances { get; set; } = [];

/// <summary>
/// Gets or sets a set of subunits of this party, which the authorized subject also has some access to.
/// </summary>
Expand Down

0 comments on commit 25f21d6

Please sign in to comment.