Skip to content

Commit

Permalink
Integrate PDP with AccessManagement PIP Delegations API (#569)
Browse files Browse the repository at this point in the history
call access management from pdp
  • Loading branch information
andreasisnes authored Jan 24, 2024
1 parent 9a6dcaf commit 9d503fa
Show file tree
Hide file tree
Showing 25 changed files with 510 additions and 161 deletions.
4 changes: 2 additions & 2 deletions src/Altinn.Authorization.ABAC/Xacml/XacmlAttributeValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ public Uri DataType
/// <summary>
/// The attributes.
/// </summary>
public ICollection<XAttribute> Attributes
public new ICollection<XAttribute> Attributes
{
get { return this.attributes; }
}

/// <summary>
/// Gets the elements.
/// </summary>
public ICollection<XElement> Elements
public new ICollection<XElement> Elements
{
get { return this.elements; }
}
Expand Down
36 changes: 36 additions & 0 deletions src/Authorization/Clients/AccessManagementClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Altinn.Platform.Authorization.Configuration;
using Microsoft.Extensions.Options;

namespace Altinn.Platform.Authorization.Clients;

/// <summary>
/// Client Configuration for Altinn Access Management API
/// </summary>
public class AccessManagementClient
{
/// <summary>
/// Gets an instance of httpclient from httpclientfactory
/// </summary>
public HttpClient Client { get; }

/// <summary>
/// Gets an instance of the configuration required by the http client
/// </summary>
public IOptions<PlatformSettings> Settings { get; }

/// <summary>
/// Initializes the HTTP client for the Altinn Access Management API
/// </summary>
/// <param name="client">The default HTTP client</param>
/// <param name="settings">the settings required by the HTTP client</param>
public AccessManagementClient(HttpClient client, IOptions<PlatformSettings> settings)
{
Settings = settings;
Client = client;
client.Timeout = new TimeSpan(0, 0, 30);
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
5 changes: 5 additions & 0 deletions src/Authorization/Configuration/PlatformSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class PlatformSettings
/// </summary>
public string AppsDomain { get; set; }

/// <summary>
/// Gets or sets the url for the Access Management API endpoint
/// </summary>
public string ApiAccessManagementEndpoint { get; set; }

/// <summary>
/// The lifetime to cache subscriptions
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Authorization/Constants/XacmlRequestAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ public static class XacmlRequestAttribute
public const string RoleAttribute = "urn:altinn:rolecode";

/// <summary>
/// Digitalt Dødsbo Role Code Attribute match identifier
/// Digitalt Dodsbo Role Code Attribute match identifier
/// </summary>
public const string OedRoleAttribute = "urn:digitaltdodsbo:rolecode";

/// <summary>
/// xacml string that represents resource
/// </summary>
public const string ResourceRegistryAttribute = "urn:altinn:resourceregistry";
public const string ResourceRegistryAttribute = "urn:altinn:resource";
}
}
190 changes: 91 additions & 99 deletions src/Authorization/Controllers/DecisionController.cs

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/Authorization/Models/AttributeMatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ namespace Altinn.Platform.Authorization.Models
/// </summary>
public class AttributeMatch
{
/// <summary>
/// // Initializes a new instance of the <see cref="AttributeMatch"/> class
/// </summary>
public AttributeMatch()
{
}

/// <summary>
/// // Initializes a new instance of the <see cref="AttributeMatch"/> class
/// </summary>
public AttributeMatch(string id, string value)
{
Id = id;
Value = value;
}

/// <summary>
/// Gets or sets the attribute id for the match
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions src/Authorization/Models/DelegationChangeInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Altinn.Platform.Authorization.Models.AccessManagement
{
/// <summary>
/// Contains attribute match info about user, reportee, resource and resourceMatchType that's being used to check all delegation changes for the resource
/// </summary>
public class DelegationChangeInput
{
/// <summary>
/// Id and value of the subject getting delegation changes info
/// </summary>
[Required]
public AttributeMatch Subject { get; set; }

/// <summary>
/// Id and value of party
/// </summary>
[Required]
public AttributeMatch Party { get; set; }

/// <summary>
/// Gets the Resource's id
/// </summary>
[Required]
public IEnumerable<AttributeMatch> Resource { get; set; }
}
}
9 changes: 4 additions & 5 deletions src/Authorization/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ void ConfigureServices(IServiceCollection services, IConfiguration config)
services.AddSingleton<IDelegationMetadataRepository, DelegationMetadataRepository>();
services.AddSingleton<IDelegationChangeEventQueue, DelegationChangeEventQueue>();
services.AddSingleton<IEventMapperService, EventMapperService>();
services.AddSingleton<IAccessManagementWrapper, AccessManagementWrapper>();

services.Configure<GeneralSettings>(config.GetSection("GeneralSettings"));
services.Configure<AzureStorageConfiguration>(config.GetSection("AzureStorageConfiguration"));
Expand All @@ -238,6 +239,7 @@ void ConfigureServices(IServiceCollection services, IConfiguration config)
services.Configure<OedAuthzMaskinportenClientSettings>(config.GetSection("OedAuthzMaskinportenClientSettings"));
services.AddMaskinportenHttpClient<SettingsJwkClientDefinition, OedAuthzMaskinportenClientDefinition>(oedAuthzMaskinportenClientSettings);
services.Configure<QueueStorageSettings>(config.GetSection("QueueStorageSettings"));
services.AddHttpClient<AccessManagementClient>();
services.AddHttpClient<IRegisterService, RegisterService>();
services.AddHttpClient<PartyClient>();
services.AddHttpClient<ProfileClient>();
Expand Down Expand Up @@ -392,9 +394,6 @@ void Configure()

app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
app.MapControllers();
app.MapHealthChecks("/health");
}
2 changes: 1 addition & 1 deletion src/Authorization/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"applicationUrl": "http://localhost:5050",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5050"
"ASPNETCORE_URLS": "http://localhost:5070"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Altinn.Platform.Authorization.Clients;
using Altinn.Platform.Authorization.Models;
using Altinn.Platform.Authorization.Models.AccessManagement;
using Altinn.Platform.Authorization.Services.Interface;
using Microsoft.Extensions.Logging;

namespace Altinn.Platform.Authorization.Services.Implementation
{
/// <summary>
/// Wrapper for the Altinn Access Management API
/// </summary>
public class AccessManagementWrapper : IAccessManagementWrapper
{
private readonly AccessManagementClient _client;
private readonly ILogger<AccessManagementWrapper> _logger;

/// <summary>
/// Initializes a new instance of the <see cref="AccessManagementWrapper"/> class.
/// </summary>
public AccessManagementWrapper(ILogger<AccessManagementWrapper> logger, AccessManagementClient client)
{
_logger = logger;
_client = client;
}

/// <summary>
/// Endpoint to find all delegation changes for a given user, reportee and app/resource context
/// </summary>
/// <returns>Input parameter to the request</returns>
public async Task<IEnumerable<DelegationChange>> GetAllDelegationChanges(DelegationChangeInput input)
{
try
{
var response = await _client.Client.SendAsync(new(HttpMethod.Post, new Uri(new Uri(_client.Settings.Value.ApiAccessManagementEndpoint), "policyinformation/getdelegationchanges"))
{
Content = new StringContent(JsonSerializer.Serialize(input), Encoding.UTF8, MediaTypeNames.Application.Json)
});

if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<IEnumerable<DelegationChange>>();
}

var content = await response.Content.ReadAsStringAsync();
throw new HttpRequestException(content == string.Empty ? $"received status code {response.StatusCode}" : content);
}
catch (HttpRequestException ex)
{
_logger.LogError("AccessManagement // AccessManagementWrapper // GetAllDelegationChanges // Failed // Unexpected Exception // Unexpected HttpStatusCode: {statusCode}\n {responseContent}", ex.StatusCode, ex.Message);
throw;
}
catch (Exception ex)
{
_logger.LogError("AccessManagement // AccessManagementWrapper // GetAllDelegationChanges // Failed // Unexpected Exception // Unexpected Message: {message}", ex.Message);
throw;
}
}

/// <summary>
/// Endpoint to find all delegation changes for a given user, reportee and app/resource context
/// </summary>
/// <param name="actions">optional funvation pattern for modifying the request sent to Access Management API</param>
/// <returns></returns>
public async Task<IEnumerable<DelegationChange>> GetAllDelegationChanges(params Action<DelegationChangeInput>[] actions)
{
var input = new DelegationChangeInput()
{
Resource = new List<AttributeMatch>(),
};

actions.ToList().ForEach(action => action(input));
return await GetAllDelegationChanges(input);
}
}
}
15 changes: 8 additions & 7 deletions src/Authorization/Services/Implementation/ContextHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Altinn.Authorization.ABAC.Constants;
using Altinn.Authorization.ABAC.Interface;
Expand Down Expand Up @@ -73,12 +74,12 @@ public ContextHandler(
/// <summary>
/// Ads needed information to the Context Request.
/// </summary>
/// <param name="request">The original Xacml Context Request</param>
/// <param name="decisionRequest">The original Xacml Context Request</param>
/// <returns>The enriched XacmlContextRequest</returns>
public async Task<XacmlContextRequest> Enrich(XacmlContextRequest request)
public async Task<XacmlContextRequest> Enrich(XacmlContextRequest decisionRequest)
{
await EnrichResourceAttributes(request);
return await Task.FromResult(request);
await EnrichResourceAttributes(decisionRequest);
return await Task.FromResult(decisionRequest);
}

/// <summary>
Expand Down Expand Up @@ -398,7 +399,7 @@ protected XacmlAttribute GetPartyIdsAttribute(List<int> partyIds)
protected async Task<List<Role>> GetRoles(int subjectUserId, int resourcePartyId)
{
string cacheKey = GetCacheKey(subjectUserId, resourcePartyId);

if (!_memoryCache.TryGetValue(cacheKey, out List<Role> roles))
{
// Key not in cache, so get data.
Expand Down Expand Up @@ -467,7 +468,7 @@ protected async Task<List<int>> GetKeyRolePartyIds(int subjectUserId)
/// </summary>
/// <param name="from">the party which the role assignment provides access on behalf of</param>
/// <param name="to">the role assignment recipient party</param>
/// <returns>list of OED/Digitalt dødsbo Role Assignments</returns>
/// <returns>list of OED/Digitalt dodsbo Role Assignments</returns>
protected async Task<List<OedRoleAssignment>> GetOedRoleAssignments(string from, string to)
{
string cacheKey = GetOedRoleassignmentCacheKey(from, to);
Expand All @@ -494,7 +495,7 @@ private string GetCacheKey(int userId, int partyId)
private string GetOedRoleassignmentCacheKey(string from, string to)
{
return $"oed{from}_{to}";
}
}

private async Task<string> GetSsnForUser(int userId)
{
Expand Down
26 changes: 26 additions & 0 deletions src/Authorization/Services/Interface/IAccessManagementWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Altinn.Platform.Authorization.Models;
using Altinn.Platform.Authorization.Models.AccessManagement;

namespace Altinn.Platform.Authorization.Services.Interface
{
/// <summary>
/// The service used to map internal delegation change to delegation change events and push them to the event queue.
/// </summary>
public interface IAccessManagementWrapper
{
/// <summary>
/// Endpoint to find all delegation changes for a given user, reportee and app/resource context
/// </summary>
/// <returns>Input parameter to the request</returns>
public Task<IEnumerable<DelegationChange>> GetAllDelegationChanges(DelegationChangeInput input);

/// <summary>
/// Endpoint to find all delegation changes for a given user, reportee and app/resource context
/// </summary>
/// <returns>optional funvation pattern for modifying the request sent to Access Management API</returns>
public Task<IEnumerable<DelegationChange>> GetAllDelegationChanges(params Action<DelegationChangeInput>[] actions);
}
}
3 changes: 2 additions & 1 deletion src/Authorization/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"ApiRegisterEndpoint": "http://localhost:5101/register/api/v1/",
"ApiStorageEndpoint": "http://localhost:5010/storage/api/v1/",
"ApiResourceRegistryEndpoint": "http://localhost:5101/resourceregistry/api/v1/",
"ApiAccessManagementEndpoint": "http://localhost:5117",
"ApiProfileEndpoint": "http://localhost:5030/profile/api/v1/"
},
"OedAuthzMaskinportenClientSettings": {
Expand All @@ -59,4 +60,4 @@
"FeatureManagement": {
"AuditLog": false
}
}
}
10 changes: 3 additions & 7 deletions test/Functions/EventPusherServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ public async Task NullDelegationChangeList_Fail()
// Arrange
EventPusherService eventPusherService = new EventPusherService(_logger.Object, _bridgeClient.Object, _eventMapperService.Object);

// Act and assert exception thrown
await Assert.ThrowsAsync<BridgeRequestFailedException>(
async () => await eventPusherService.PushEvents(null));
await Assert.ThrowsAsync<BridgeRequestFailedException>(() => eventPusherService.PushEvents(null));
}

[Fact]
Expand All @@ -72,8 +70,7 @@ public async Task PostDelegationEvents_Non200_Fail()
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.BadRequest));

// Act and assert exception thrown
await Assert.ThrowsAsync<BridgeRequestFailedException>(
async () => await eventPusherService.PushEvents(GetDelegationChangeList()));
await Assert.ThrowsAsync<BridgeRequestFailedException>(() => eventPusherService.PushEvents(GetDelegationChangeList()));
}

[Fact]
Expand All @@ -85,8 +82,7 @@ public async Task PostDelegationEvents_Exception_Fail()
.ThrowsAsync(new HttpRequestException());

// Act and assert exception thrown
await Assert.ThrowsAsync<BridgeRequestFailedException>(
async () => await eventPusherService.PushEvents(GetDelegationChangeList()));
await Assert.ThrowsAsync<BridgeRequestFailedException>(() => eventPusherService.PushEvents(GetDelegationChangeList()));
}

private static DelegationChangeEventList GetDelegationChangeList()
Expand Down
Loading

0 comments on commit 9d503fa

Please sign in to comment.