Skip to content

Commit

Permalink
Code moved from api to server project
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurt Monge committed Nov 29, 2024
1 parent d56510b commit e68d33c
Show file tree
Hide file tree
Showing 24 changed files with 1,067 additions and 65 deletions.
33 changes: 0 additions & 33 deletions src/oed-testdata.Server/Controllers/WeatherForecastController.cs

This file was deleted.

76 changes: 76 additions & 0 deletions src/oed-testdata.Server/Infrastructure/Altinn/AltinnAuthHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Net.Http.Headers;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

namespace oed_testdata.Server.Infrastructure.Altinn
{
public class AltinnAuthHandler(
IHttpClientFactory factory,
IOptionsMonitor<AltinnSettings> options,
IMemoryCache cache)
: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Headers.Authorization is not null && request.Headers.Authorization.Scheme == "Bearer")
{
return await base.SendAsync(request, cancellationToken);
}

// Do auth, add auth header and try again
var token = await GetCachedEnterpriseToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await base.SendAsync(request, cancellationToken);
}

private async Task<string> GetCachedEnterpriseToken()
{
// TODO: Build key from params
var cacheKey = "key";

var token = await cache.GetOrCreateAsync(cacheKey, async (entry) =>
{
return await GetEnterpriseToken();
},
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(86300)
});

return token;
}

private async Task<string> GetEnterpriseToken()
{
var scopes = "altinn:serviceowner/instances.read altinn:serviceowner/instances.write altinn:lookup";

var queryParams = new Dictionary<string, string?>
{
{ "env", "tt02" },
{ "orgNo", "991825827" },
{ "org", "digdir" },
{ "ttl", "86400" },
//{ "partyId", "50552094" },
{ "scopes", scopes }
};

var baseUri = new Uri(options.CurrentValue.TokenGeneratorUrl, UriKind.Absolute);
var pathWithQuery = QueryHelpers.AddQueryString("/api/GetEnterpriseToken", queryParams);
var requestUri = new Uri(baseUri, pathWithQuery);

var auth = $"{options.CurrentValue.Username}:{options.CurrentValue.Password}";
var encodedAuth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(auth));

var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth);

var httpClient = factory.CreateClient(nameof(AltinnAuthHandler));
var response = await httpClient.SendAsync(request);

var token = await response.Content.ReadAsStringAsync();

return token;
}
}
}
65 changes: 65 additions & 0 deletions src/oed-testdata.Server/Infrastructure/Altinn/AltinnClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Altinn.Platform.Storage.Interface.Models;
using Microsoft.Extensions.Options;
using oed_testdata.Server.Infrastructure.Altinn.Models;

namespace oed_testdata.Server.Infrastructure.Altinn;

public interface IAltinnClient
{
public Task<List<Instance>> GetOedInstances();
public Task<List<Instance>> GetOedInstancesByDeceasedNin(string deceasedNin);

public Task<T> GetOedInstanceData<T>(string instanceId, string instanceDataId);
}

public class AltinnClient(
HttpClient httpClient,
IOptionsMonitor<AltinnSettings> options)
: IAltinnClient
{
public async Task<List<Instance>> GetOedInstances()
{
var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute);
var requestUri = new Uri(baseUri, "/storage/api/v1/instances?org=digdir&appId=digdir/oed&status.isHardDeleted=false&status.isSoftDeleted=false&size=50");

var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await httpClient.SendAsync(request);

var s = response.Content.ReadAsStringAsync();

await using var contentStream = await response.Content.ReadAsStreamAsync();
var altinnResponse = await AltinnJsonSerializer.Deserialize<AltinnInstancesResponse>(contentStream);

return altinnResponse.Instances;
}

public async Task<List<Instance>> GetOedInstancesByDeceasedNin(string deceasedNin)
{
var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute);
var requestUri = new Uri(baseUri, "/storage/api/v1/instances?org=digdir&appId=digdir/oed&status.isHardDeleted=false&status.isSoftDeleted=false");

var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.TryAddWithoutValidation("X-Ai-InstanceOwnerIdentifier", $"person:{deceasedNin}");
var response = await httpClient.SendAsync(request);

await using var contentStream = await response.Content.ReadAsStreamAsync();
var altinnResponse = await AltinnJsonSerializer.Deserialize<AltinnInstancesResponse>(contentStream);

return altinnResponse.Instances;
}

public async Task<T> GetOedInstanceData<T>(string instanceId, string instanceDataId)
{
var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute);
var requestUri = new Uri(baseUri, $"https://platform.tt02.altinn.no/storage/api/v1/instances/{instanceId}/data/{instanceDataId}");

var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var response = await httpClient.SendAsync(request);

await using var contentStream = await response.Content.ReadAsStreamAsync();
var data = AltinnXmlSerializer.Deserialize<T>(contentStream);

return data;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Xml.Serialization;
using Newtonsoft.Json;

namespace oed_testdata.Server.Infrastructure.Altinn
{
public static class AltinnJsonSerializer
{
public static async Task<T> Deserialize<T>(Stream contentStream)
{
var serializer = new JsonSerializer();

using var sr = new StreamReader(contentStream);
await using var jsonTextReader = new JsonTextReader(sr);
return serializer.Deserialize<T>(jsonTextReader)!;
}
}

public static class AltinnXmlSerializer
{
public static T Deserialize<T>(Stream contentStream)
{
var serializer = new XmlSerializer(typeof(T));
var dataObject = serializer.Deserialize(contentStream);

var data = (T)dataObject!;
return data;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace oed_testdata.Server.Infrastructure.Altinn;

public class AltinnSettings
{
public required string TokenGeneratorUrl { get; set; }
public required string PlatformUrl { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Net.Http.Headers;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

namespace oed_testdata.Server.Infrastructure.Altinn
{
public class MaskinportenAuthHandler(
IHttpClientFactory factory,
IOptionsMonitor<AltinnSettings> options,
IMemoryCache cache)
: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Headers.Authorization is not null && request.Headers.Authorization.Scheme == "Bearer")
{
return await base.SendAsync(request, cancellationToken);
}

// Do auth, add auth header and try again
var token = await GetCachedEnterpriseToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await base.SendAsync(request, cancellationToken);
}

private async Task<string> GetCachedEnterpriseToken()
{
// TODO: Build key from params
var cacheKey = "key";

var token = await cache.GetOrCreateAsync(cacheKey, async (entry) =>
{
return await GetEnterpriseToken();
},
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(86300)
});

return token;
}

private async Task<string> GetEnterpriseToken()
{
var scopes = "altinn:serviceowner/instances.read altinn:serviceowner/instances.write altinn:lookup";

var queryParams = new Dictionary<string, string?>
{
{ "env", "tt02" },
{ "orgNo", "991825827" },
{ "org", "digdir" },
{ "ttl", "86400" },
//{ "partyId", "50552094" },
{ "scopes", scopes }
};

var baseUri = new Uri(options.CurrentValue.TokenGeneratorUrl, UriKind.Absolute);
var pathWithQuery = QueryHelpers.AddQueryString("/api/GetEnterpriseToken", queryParams);
var requestUri = new Uri(baseUri, pathWithQuery);

var auth = $"{options.CurrentValue.Username}:{options.CurrentValue.Password}";
var encodedAuth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(auth));

var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth);

var httpClient = factory.CreateClient(nameof(MaskinportenAuthHandler));
var response = await httpClient.SendAsync(request);

var token = await response.Content.ReadAsStringAsync();

return token;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
using Altinn.Platform.Storage.Interface.Models;
using Newtonsoft.Json;

namespace oed_testdata.Server.Infrastructure.Altinn.Models
{
public class AltinnInstancesResponse
{
[JsonProperty("count")]
public int Count { get; set; }

[JsonPropertyName("self")]
public string Self { get; set; }

[JsonPropertyName("next")]
public string Next { get; set; }

[JsonPropertyName("instances")]
public List<Instance> Instances { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace oed_testdata.Server.Infrastructure.Altinn;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddAltinnClient(
this IServiceCollection services,
IConfiguration configuration)
{
services.Configure<AltinnSettings>(configuration.GetSection("AltinnSettings"));

services
.AddMemoryCache()
.AddTransient<AltinnAuthHandler>()
.AddHttpClient<IAltinnClient, AltinnClient>()
.AddHttpMessageHandler<AltinnAuthHandler>();

return services;
}
}
Loading

0 comments on commit e68d33c

Please sign in to comment.