Skip to content
Open
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
Expand Up @@ -592,6 +592,13 @@ public async Task<IActionResult> AddResourceRules([FromQuery] ConnectionInput co
return Problem();
}

// Validate delegation authorization
var delegationAuthorizationError = await ValidateResourceDelegationAuthorization(fromId, resource, actionKeys.DirectRuleKeys, cancellationToken);
if (delegationAuthorizationError is { })
{
return delegationAuthorizationError;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delegation check'en gjøres vel nå allerede inne i ConnectionService.AddResource : 607
så ville i utgangspunktet forvente at det er responsen der i fra som returnerte feilmodellen i stedet for å gjøre en ekstra sjekk i kontrolleren først.

}

var from = await EntityService.GetEntity(fromId, cancellationToken);
var to = await EntityService.GetEntity(toId, cancellationToken);
var by = await EntityService.GetEntity(byId, cancellationToken);
Expand Down Expand Up @@ -634,6 +641,13 @@ public async Task<IActionResult> UpdateResourceRules([FromQuery] ConnectionInput
return Problem();
}

// Validate delegation authorization
var delegationAuthorizationError = await ValidateResourceDelegationAuthorization(fromId, resource, updateDto.DirectRuleKeys, cancellationToken);
if (delegationAuthorizationError is { })
{
return delegationAuthorizationError;
}

var from = await EntityService.GetEntity(fromId, cancellationToken);
var to = await EntityService.GetEntity(toId, cancellationToken);
var by = await EntityService.GetEntity(byId, cancellationToken);
Expand Down Expand Up @@ -708,5 +722,41 @@ public async Task<IActionResult> CheckResource([FromQuery] Guid party, [FromQuer
return Ok(result.Value);
}

/// <summary>
/// Validates that the authenticated user has permission to delegate all requested rules for a resource.
/// </summary>
/// <param name="fromId">The party delegating from.</param>
/// <param name="resource">The resource identifier.</param>
/// <param name="requestedRuleKeys">The rule keys that should be delegated.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// An <see cref="IActionResult"/> containing validation errors if delegation is not authorized,
/// or <c>null</c> if all rules can be delegated.
/// </returns>
private async Task<IActionResult?> ValidateResourceDelegationAuthorization(
Guid fromId,
string resource,
IEnumerable<string> requestedRuleKeys,
CancellationToken cancellationToken)
{
var byId = AuthenticationHelper.GetPartyUuid(this.HttpContext);

var delegationCheck = await ConnectionService.ResourceDelegationCheck(byId, fromId, resource, ConfigureConnections, cancellationToken);
if (delegationCheck.IsProblem)
{
return delegationCheck.Problem.ToActionResult();
}

var validationErrors = ValidationComposer.Validate(
ConnectionValidation.ValidateDelegationAuthorization(requestedRuleKeys, delegationCheck.Value));

if (validationErrors is { })
{
return validationErrors.ToActionResult();
}

return null;
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Altinn.AccessManagement.Core.Errors;
using Altinn.AccessMgmt.Core.Validation;
using Altinn.Authorization.Api.Contracts.AccessManagement;
using Altinn.Authorization.ProblemDetails;

namespace Altinn.AccessManagement.Api.Enduser.Validation;
Expand Down Expand Up @@ -164,4 +165,34 @@ internal static RuleExpression ExclusivePackageReference(Guid? packageId, string
errors.Add(ValidationErrors.InvalidQueryParameter, $"QUERY/{urnName}", [new(urnName, ValidationErrorMessageTexts.RequireOnePackageRef)]);
};
};

/// <summary>
/// Validates that user is authorized to delegate all requested rules based on delegation check results.
/// </summary>
/// <param name="requestedRules">The rule keys that the user wants to delegate.</param>
/// <param name="delegationCheckResult">The result from ResourceDelegationCheck indicating which rules can be delegated.</param>
internal static RuleExpression DelegationAuthorization(IEnumerable<string> requestedRules, ResourceCheckDto delegationCheckResult) => () =>
{
var failedRules = new List<string>();
foreach (var ruleKey in requestedRules)
{
if (!delegationCheckResult.Rules.Any(r => r.Rule.Key == ruleKey && r.Result))
{
failedRules.Add(ruleKey);
}
}

if (failedRules.Count == 0)
{
return null;
}

return (ref ValidationErrorBuilder errors) =>
{
foreach (var failedRule in failedRules)
{
errors.Add(ValidationErrors.UserNotAuthorized, $"$BODY/directRuleKeys/{failedRule}", [new("ruleKey", ValidationErrorMessageTexts.NotAuthorizedToDelegateRule)]);
}
};
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Altinn.AccessManagement.Api.Enduser.Models;
using Altinn.AccessManagement.Core.Errors;
using Altinn.AccessMgmt.Core.Validation;
using Altinn.Authorization.Api.Contracts.AccessManagement;
using Altinn.Authorization.ProblemDetails;

namespace Altinn.AccessManagement.Api.Enduser.Validation;

Expand Down Expand Up @@ -128,4 +131,14 @@ internal static RuleExpression ValidateRemoveResourceFromConnection(string party
ParameterValidation.PartyTo(to),
ConnectionCombinationRules.RemovePartyMatchesFromOrTo(party, from, to)
);

/// <summary>
/// Validation rule for delegation check - validates that user is authorized to delegate all requested rules.
/// </summary>
/// <param name="requestedRules">The rule keys that the user wants to delegate.</param>
/// <param name="delegationCheckResult">The result from ResourceDelegationCheck indicating which rules can be delegated.</param>
internal static RuleExpression ValidateDelegationAuthorization(IEnumerable<string> requestedRules, ResourceCheckDto delegationCheckResult) =>
ValidationComposer.All(
ConnectionCombinationRules.DelegationAuthorization(requestedRules, delegationCheckResult)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ internal static class ValidationErrorMessageTexts
internal const string PersonIdentifierInvalid = "Invalid national identity number";
internal const string LastNameRequired = "Required when providing PersonInput details";
internal const string PersonIdentifierLastNameInvalid = "PersonInput details must match";
internal const string NotAuthorizedToDelegateRule = "You do not have permission to delegate this rule";
internal const string SelfDelegationNotAllowed = "Self-delegation not allowed. From and To cannot be the same party";
}
Loading