Skip to content

Commit

Permalink
Feature/843 revoke instance delegation2 (#857)
Browse files Browse the repository at this point in the history
* Unsaved files

* Push before stashrecovery

* Stash recovery

* Removed RevokeAllEndpoint

* Removed unused method

* Added test and fixed some duplicate code

* Fixed some code smells
Added Authorization removed for local test

* Unsaved changes missed from merge
  • Loading branch information
lovoll authored Oct 29, 2024
1 parent f09d845 commit 5dbb8aa
Show file tree
Hide file tree
Showing 28 changed files with 883 additions and 77 deletions.
18 changes: 18 additions & 0 deletions src/Altinn.AccessManagement.Core/Enums/RevokeStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Altinn.AccessManagement.Core.Enums
{
/// <summary>
/// Enum for different right revoke status responses
/// </summary>
public enum RevokeStatus
{
/// <summary>
/// Right was not revoked
/// </summary>
NotRevoked = 0,

/// <summary>
/// Right was revoked
/// </summary>
Revoked = 1
}
}
61 changes: 59 additions & 2 deletions src/Altinn.AccessManagement.Core/Helpers/DelegationHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Text;
using System.Text;
using Altinn.AccessManagement.Core.Constants;
using Altinn.AccessManagement.Core.Enums;
using Altinn.AccessManagement.Core.Helpers.Extensions;
Expand Down Expand Up @@ -531,6 +530,34 @@ public static bool PolicyContainsMatchingInstanceRule(XacmlPolicy policy, Instan
return false;
}

/// <summary>
/// Returns Xacml rule in the provided XacmlPolicy that contains a rule having an identical Resource signature and contains the Action from the InstanceRule null if no match,
/// to be used for finding rules to revoke.
/// </summary>
/// <returns>A bool</returns>
public static XacmlRule GetXamlRuleContainsMatchingInstanceRule(XacmlPolicy policy, InstanceRule rule)
{
string ruleResourceKey = GetAttributeMatchKey(rule.Resource.ToList());

foreach (XacmlRule policyRule in policy.Rules)
{
if (!policyRule.Effect.Equals(XacmlEffectType.Permit) || policyRule.Target == null)
{
continue;
}

bool matchingActionFound = MatchingActionFound(policyRule.Target.AnyOf, rule, out List<List<AttributeMatch>> policyResourceMatches);

if (policyResourceMatches.Exists(resourceMatch => GetAttributeMatchKey(resourceMatch) == ruleResourceKey) && matchingActionFound)
{
rule.RuleId = policyRule.RuleId;
return policyRule;
}
}

return null;
}

private static bool MatchingActionFound(ICollection<XacmlAnyOf> input, InstanceRule rule, out List<List<AttributeMatch>> policyResourceMatches)
{
policyResourceMatches = [];
Expand Down Expand Up @@ -839,6 +866,21 @@ public static IEnumerable<InstanceRightDelegationResult> GetRightDelegationResul
});
}

/// <summary>
/// Gets the list of Rules as a list of RightDelegationResult
/// </summary>
/// <param name="rules">The rules output from a delegation to convert</param>
/// <returns>List of RightDelegationResult</returns>
public static IEnumerable<InstanceRightRevokeResult> GetRightRevokeResultsFromInstanceRules(InstanceRight rules)
{
return rules.InstanceRules.Select(rule => new InstanceRightRevokeResult
{
Resource = rule.Resource,
Action = rule.Action,
Status = rule.CreatedSuccessfully ? RevokeStatus.Revoked : RevokeStatus.NotRevoked
});
}

/// <summary>
/// Gets the list of Rights as a list of RightDelegationResult
/// </summary>
Expand Down Expand Up @@ -888,6 +930,21 @@ public static (UuidType DelegationType, Guid Uuid) GetUuidTypeAndValueFromParty(
return (UuidType.NotSpecified, Guid.Empty);
}

/// <summary>
/// Gets the list of Rights as a list of RightDelegationResult
/// </summary>
/// <param name="rights">The rights to convert</param>
/// <returns>List of RightDelegationResult</returns>
public static IEnumerable<InstanceRightRevokeResult> GetRightRevokeResultsFromFailedInternalRights(List<RightInternal> rights)
{
return rights.Select(right => new InstanceRightRevokeResult
{
Resource = right.Resource,
Action = right.Action,
Status = RevokeStatus.NotRevoked
});
}

private static void SetTypeForSingleRule(List<int> keyRolePartyIds, int offeredByPartyId, List<AttributeMatch> coveredBy, int parentPartyId, Rule rule, int? coveredByPartyId, int? coveredByUserId)
{
bool isUserId = TryGetUserIdFromAttributeMatch(coveredBy, out int coveredByUserIdFromRequest);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.ComponentModel.DataAnnotations;
using Altinn.AccessManagement.Core.Enums;
using Altinn.AccessManagement.Core.Models.Register;

namespace Altinn.AccessManagement.Core.Models;

/// <summary>
/// Response model for performing revoke of access to a respource from Apps
/// </summary>
public class AppsInstanceRevokeResponse
{
/// <summary>
/// Gets or sets the urn identifying the party to delegate from
/// </summary>
[Required]
public PartyUrn From { get; set; }

/// <summary>
/// Gets or sets the urn identifying the party to be delegated to
/// </summary>
[Required]
public PartyUrn To { get; set; }

/// <summary>
/// Gets or sets the urn identifying the resource of the instance
/// </summary>
[Required]
public string ResourceId { get; set; }

/// <summary>
/// Gets or sets the urn identifying the instance id
/// </summary>
[Required]
public string InstanceId { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the instance delegation is for a parallel task
/// </summary>
[Required]
public InstanceDelegationMode InstanceDelegationMode { get; set; }

/// <summary>
/// Gets or sets the rights to delegate
/// </summary>
[Required]
public IEnumerable<InstanceRightRevokeResult> Rights { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Altinn.AccessManagement.Core.Enums;
using Altinn.AccessManagement.Core.Models.Rights;
using Altinn.Urn.Json;

namespace Altinn.AccessManagement.Core.Models;

/// <summary>
/// This model describes a single right
/// </summary>
public class InstanceRightRevokeResult
{
/// <summary>
/// Gets or sets the list of resource matches which uniquely identifies the resource this right applies to.
/// </summary>
public List<UrnJsonTypeValue> Resource { get; set; }

/// <summary>
/// Gets or sets the set of Attribute Id and Attribute Value for a specific action, to identify the action this right applies to
/// </summary>
public UrnJsonTypeValue<ActionUrn> Action { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the right was successfully revoked or not
/// </summary>
public RevokeStatus Status { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,61 @@ public async Task<Result<ResourceDelegationCheckResponse>> DelegationCheck(AppsI

/// <inheritdoc/>
public async Task<Result<AppsInstanceDelegationResponse>> Delegate(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default)
{
(ValidationErrorBuilder Errors, InstanceRight RulesToHandle, List<RightInternal> RightsAppCantHandle) input = await SetUpDelegationOrRevokeRequest(request, cancellationToken);

if (input.Errors.TryBuild(out var errorResult))
{
return errorResult;
}

AppsInstanceDelegationResponse result = new()
{
From = request.From,
To = request.To,
ResourceId = request.ResourceId,
InstanceId = request.InstanceId,
InstanceDelegationMode = request.InstanceDelegationMode
};

List<InstanceRightDelegationResult> rights = await DelegateRights(input.RulesToHandle, input.RightsAppCantHandle, cancellationToken);
result.Rights = rights;
result = RemoveInstanceIdFromResourceForDelegationResponse(result);

return result;
}

/// <inheritdoc/>
public async Task<Result<AppsInstanceRevokeResponse>> Revoke(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default)
{
(ValidationErrorBuilder Errors, InstanceRight RulesToHandle, List<RightInternal> RightsAppCantHandle) input = await SetUpDelegationOrRevokeRequest(request, cancellationToken);

if (input.Errors.TryBuild(out var errorResult))
{
return errorResult;
}

AppsInstanceRevokeResponse result = new()
{
From = request.From,
To = request.To,
ResourceId = request.ResourceId,
InstanceId = request.InstanceId,
InstanceDelegationMode = request.InstanceDelegationMode
};

List<InstanceRightRevokeResult> rights = await RevokeRights(input.RulesToHandle, input.RightsAppCantHandle, cancellationToken);
result.Rights = rights;
result = RemoveInstanceIdFromResourceForRevokeResponse(result);

return result;
}

private async Task<(ValidationErrorBuilder Errors, InstanceRight RulesToHandle, List<RightInternal> RightsAppCantHandle)> SetUpDelegationOrRevokeRequest(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default)
{
ValidationErrorBuilder errors = default;
List<RightInternal> rightsAppCantHandle = null;
InstanceRight rulesToHandle = null;

// Fetch from and to from partyuuid
(UuidType Type, Guid? Uuid) from = await TranslatePartyUuidToPersonOrganizationUuid(request.From);
Expand Down Expand Up @@ -273,13 +326,14 @@ public async Task<Result<AppsInstanceDelegationResponse>> Delegate(AppsInstanceD
}
}

if (errors.TryBuild(out var errorResult))
{
return errorResult;
if (!errors.IsEmpty)
{
return (errors, rulesToHandle, rightsAppCantHandle);
}

// Perform delegation
InstanceRight rulesToDelegate = new InstanceRight
rightsAppCantHandle = [];

rulesToHandle = new InstanceRight
{
FromUuid = (Guid)from.Uuid,
FromType = from.Type,
Expand All @@ -292,46 +346,45 @@ public async Task<Result<AppsInstanceDelegationResponse>> Delegate(AppsInstanceD
InstanceDelegationMode = request.InstanceDelegationMode,
InstanceDelegationSource = request.InstanceDelegationSource,
};
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)
UrnJsonTypeValue instanceId = KeyValueUrn.CreateUnchecked($"{AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute}:{request.InstanceId}", AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute.Length + 1);

foreach (RightInternal rightToHandle in request.Rights)
{
if (CheckIfInstanceIsDelegable(delegableRights, rightToDelegate))
if (CheckIfInstanceIsDelegable(delegableRights, rightToHandle))
{
rightToDelegate.Resource.Add(instanceId);
rulesToDelegate.InstanceRules.Add(new InstanceRule
rightToHandle.Resource.Add(instanceId);
rulesToHandle.InstanceRules.Add(new InstanceRule
{
Resource = rightToDelegate.Resource,
Action = rightToDelegate.Action
Resource = rightToHandle.Resource,
Action = rightToHandle.Action
});
}
else
{
rightsAppCantDelegate.Add(rightToDelegate);
rightsAppCantHandle.Add(rightToHandle);
}
}

AppsInstanceDelegationResponse result = new()
{
From = request.From,
To = request.To,
ResourceId = request.ResourceId,
InstanceId = request.InstanceId,
InstanceDelegationMode = request.InstanceDelegationMode
};

List<InstanceRightDelegationResult> rights = await DelegateRights(rulesToDelegate, rightsAppCantDelegate, cancellationToken);
result.Rights = rights;
result = RemoveInstanceIdFromResourceForResponse(result);

return result;
return (errors, rulesToHandle, rightsAppCantHandle);
}

/// <inheritdoc/>
public Task<Result<AppsInstanceDelegationResponse>> Revoke(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default)
private async Task<List<InstanceRightRevokeResult>> RevokeRights(InstanceRight rulesToRevoke, List<RightInternal> rightsAppCantRevoke, CancellationToken cancellationToken)
{
throw new NotImplementedException();
List<InstanceRightRevokeResult> rights = new List<InstanceRightRevokeResult>();

if (rulesToRevoke.InstanceRules.Count > 0)
{
InstanceRight delegationResult = await _pap.TryWriteInstanceRevokePolicyRules(rulesToRevoke, cancellationToken);
rights.AddRange(DelegationHelper.GetRightRevokeResultsFromInstanceRules(delegationResult));
}

if (rightsAppCantRevoke.Count > 0)
{
rights.AddRange(DelegationHelper.GetRightRevokeResultsFromFailedInternalRights(rightsAppCantRevoke));
}

return rights;
}

private async Task<List<InstanceRightDelegationResult>> DelegateRights(InstanceRight rulesToDelegate, List<RightInternal> rightsAppCantDelegate, CancellationToken cancellationToken)
Expand Down Expand Up @@ -397,21 +450,31 @@ public async Task<Result<List<AppsInstanceDelegationResponse>>> Get(AppsInstance
}

List<AppsInstanceDelegationResponse> result = await _pip.GetInstanceDelegations(request, cancellationToken);
result = RemoveInstanceIdFromResourceForResponseList(result);
result = RemoveInstanceIdFromResourceForDelegationResponseList(result);
return result;
}

private static AppsInstanceRevokeResponse RemoveInstanceIdFromResourceForRevokeResponse(AppsInstanceRevokeResponse input)
{
foreach (var right in input.Rights)
{
right.Resource.RemoveAll(r => r.HasValue && r.Value.PrefixSpan.ToString() == AltinnXacmlConstants.MatchAttributeIdentifiers.ResourceInstanceAttribute);
}

return input;
}

private static List<AppsInstanceDelegationResponse> RemoveInstanceIdFromResourceForResponseList(List<AppsInstanceDelegationResponse> input)
private static List<AppsInstanceDelegationResponse> RemoveInstanceIdFromResourceForDelegationResponseList(List<AppsInstanceDelegationResponse> input)
{
foreach (AppsInstanceDelegationResponse item in input)
{
RemoveInstanceIdFromResourceForResponse(item);
RemoveInstanceIdFromResourceForDelegationResponse(item);
}

return input;
}

private static AppsInstanceDelegationResponse RemoveInstanceIdFromResourceForResponse(AppsInstanceDelegationResponse input)
private static AppsInstanceDelegationResponse RemoveInstanceIdFromResourceForDelegationResponse(AppsInstanceDelegationResponse input)
{
foreach (var right in input.Rights)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public interface IAppsInstanceDelegationService
/// <param name="request">the request data collected in a dto</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/></param>
/// <returns>Boolean whether the app instance delegation was revoked</returns>
public Task<Result<AppsInstanceDelegationResponse>> Revoke(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default);
public Task<Result<AppsInstanceRevokeResponse>> Revoke(AppsInstanceDelegationRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Gets app instance delegation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ public interface IPolicyAdministrationPoint
/// <returns>The list of instance rules with created Id and result status</returns>
Task<InstanceRight> TryWriteInstanceDelegationPolicyRules(InstanceRight rules, CancellationToken cancellationToken = default);

/// <summary>
/// Trys to sort and revoke the set of rules as delegation policy files in blob storage.
/// </summary>
/// <param name="rules">The set of instance rules to be delegated</param>
/// <param name="cancellationToken">CancellationToke</param>
/// <returns>The list of instance rules with created Id and result status</returns>
Task<InstanceRight> TryWriteInstanceRevokePolicyRules(InstanceRight rules, CancellationToken cancellationToken = default);

/// <summary>
/// Trys to sort and delete the set of rules matching the list of ruleMatches to delete from delegation policy files in blob storage.
/// </summary>
Expand Down
Loading

0 comments on commit 5dbb8aa

Please sign in to comment.