Skip to content

Commit

Permalink
feat: add fetching of kofuvi addresses for br org (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
SondreJDigdir authored Sep 10, 2024
1 parent d84d058 commit 83671e5
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 12 deletions.
47 changes: 47 additions & 0 deletions src/Dan.Plugin.Tilda/Config/KeyVault.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace Dan.Plugin.Tilda.Config;

public class KeyVault
{
private SecretClient SecretClient { get; }

/// <summary>
/// Key Vault for Core
/// </summary>
/// <param name="vaultName">Name of the Key Vault</param>
public KeyVault(string vaultName)
{
SecretClient = new SecretClient(new Uri($"https://{vaultName}.vault.azure.net/"), new DefaultAzureCredential());
}

/// <summary>
/// Get a secret from the key vault
/// </summary>
/// <param name="key">Secret name</param>
/// <returns>The secret value</returns>
public async Task<string> Get(string key)
{
var secret = await SecretClient.GetSecretAsync(key);
return secret.Value.Value;
}

/// <summary>
/// Get a certificate from the key vault
/// </summary>
/// <param name="key">Certificate name</param>
/// <returns>The certificate</returns>
public async Task<X509Certificate2> GetCertificate(string key)
{
var base64Certificate = await Get(key);
var certBytes = Convert.FromBase64String(base64Certificate);

var cert = new X509Certificate2(certBytes, string.Empty, X509KeyStorageFlags.MachineKeySet);

return await Task.FromResult(cert);
}
}
23 changes: 22 additions & 1 deletion src/Dan.Plugin.Tilda/Config/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Security.Cryptography.X509Certificates;

namespace Dan.Plugin.Tilda.Config
{
Expand All @@ -10,12 +11,32 @@ public string GetClassBaseUri(string className)
}

public string RedisConnectionString { get; set; }

public bool IsTest { get; set; }

public bool IsLocalDevelopment { get; set; }

public string Breaker_RetryWaitTime { get; set; }
public string Breaker_OpenCircuitTime { get; set; }

public string KofuviEndpoint { get; set; }
public string KvName { get; set; }
public string KvKofuviCertificateName { get; set; }

private static string KeyVaultName => Environment.GetEnvironmentVariable("KvName");
private static string KofuviCertificateName => Environment.GetEnvironmentVariable("KvKofuviCertificateName");

private static X509Certificate2 _altinnCertificate { get; set; }
public static X509Certificate2 Certificate
{
get
{
return _altinnCertificate ?? new KeyVault(KeyVaultName).GetCertificate(KofuviCertificateName).Result;
}
set
{
_altinnCertificate = value;
}
}
}
}
2 changes: 2 additions & 0 deletions src/Dan.Plugin.Tilda/Dan.Plugin.Tilda.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.11.4" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.6.0" />
<PackageReference Include="Dan.Common" Version="1.5.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
Expand Down
54 changes: 54 additions & 0 deletions src/Dan.Plugin.Tilda/Models/KofuviResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Dan.Plugin.Tilda.Models;

[Serializable]
public class KofuviResponse
{
[JsonProperty("_embedded")]
public Embedded Embedded { get; set; }
}

[Serializable]
public class Embedded
{
[JsonProperty("varsling")]
public Notification Notification { get; set; }
}

[Serializable]
public class Notification
{
[JsonProperty("varslingsadresser")]
public List<NotificationAddress> NotificationAddresses { get; set; }
}

[Serializable]
public class NotificationAddress
{
[JsonProperty("kontaktinformasjon")]
public ContactInformation ContactInformation { get; set; }
}

[Serializable]
public class ContactInformation
{
[JsonProperty("digitalVarslingsinformasjon")]
public DigitalNotificationInformation DigitalNotificationInformation { get; set; }
}

[Serializable]
public class DigitalNotificationInformation
{
[JsonProperty("epostadresse")]
public NotificationEmail NotificationEmail { get; set; }
}

[Serializable]
public class NotificationEmail
{
[JsonProperty("fullstendigAdresse")]
public string CompleteEmail { get; set; }
}
5 changes: 3 additions & 2 deletions src/Dan.Plugin.Tilda/Models/TildaRegistryEntry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;

namespace Dan.Plugin.Tilda.Models
Expand All @@ -14,6 +13,9 @@ public class TildaRegistryEntry
[JsonProperty("tildaenhetNavn", Required = Required.Default, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Name;

[JsonProperty("epostaddresser", Required = Required.Default, DefaultValueHandling = DefaultValueHandling.Ignore)]
public List<string> Emails;

[JsonProperty("tildaenhetHovedenhet", Required = Required.Default, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string ControlObjectParent;

Expand All @@ -31,7 +33,6 @@ public class TildaRegistryEntry

[JsonProperty("driftsstatus", Required = Required.Default, DefaultValueHandling = DefaultValueHandling.Ignore)]
public OperationStatus OperationalStatus;

}

public class AccountsInformation
Expand Down
15 changes: 13 additions & 2 deletions src/Dan.Plugin.Tilda/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Polly.Extensions.Http;
using Polly.Registry;
using System;
using System.Net.Http;
using Settings = Dan.Plugin.Tilda.Config.Settings;

var host = new HostBuilder()
Expand All @@ -20,7 +21,7 @@
{
configuration
.AddJsonFile("worker-logging.json", optional:true);
})
})
.ConfigureServices((context, services) =>
{
// This makes IOption<Settings> available in the DI container.
Expand All @@ -47,8 +48,18 @@
services.AddHttpClient("ERHttpClient", client =>
{
client.Timeout = new TimeSpan(0, 0, 5);
});
});

services.AddHttpClient("KofuviClient", _ =>
{

})
.ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(Settings.Certificate);
return handler;
});;

})
.Build();
Expand Down
26 changes: 21 additions & 5 deletions src/Dan.Plugin.Tilda/Tilda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class Tilda
private ILogger _logger;
private HttpClient _client;
private HttpClient _erClient;
private HttpClient _kofuviClient;
private Settings _settings;
private readonly IEntityRegistryService _entityRegistryService;
private readonly IEvidenceSourceMetadata _metadata;
Expand All @@ -41,6 +42,7 @@ public Tilda(IHttpClientFactory httpClientFactory, IOptions<Settings> settings,
_policyRegistry = policyRegistry;
_client = httpClientFactory.CreateClient("SafeHttpClient");
_erClient = httpClientFactory.CreateClient("ERHttpClient");
_kofuviClient = httpClientFactory.CreateClient("KofuviClient");
_settings = settings.Value;
_entityRegistryService = entityRegistry;
_entityRegistryService.AllowTestCcrLookup = _settings.IsLocalDevelopment || _settings.IsLocalDevelopment;
Expand Down Expand Up @@ -440,6 +442,7 @@ private async Task<List<EvidenceValue>> GetEvidenceValuesStorulykkevirksomhet(Ev
return eb.GetEvidenceValues();
}

// TODO: use IncludeSubunits-param in Tildaparameters? And then we can reduce duplicate code between the overloads
private async Task<List<TildaRegistryEntry>> GetOrganizationsFromBR(string organizationNumber, TildaParameters param)
{
var result = new List<TildaRegistryEntry>();
Expand All @@ -450,19 +453,32 @@ private async Task<List<TildaRegistryEntry>> GetOrganizationsFromBR(string organ
accountsInformation = await Helpers.GetAnnualTurnoverFromBR(organizationNumber, _client, _policyRegistry);
}

result.Add(await ConvertBRtoTilda(brResult.First(), accountsInformation));
var kofuviAddresses = await Helpers.GetKofuviAddresses(_settings.KofuviEndpoint, organizationNumber, _kofuviClient, _logger);

var organization = await ConvertBRtoTilda(brResult.First(), accountsInformation);
if (kofuviAddresses.Count > 0)
{
organization.Emails = kofuviAddresses;
}
result.Add(organization);

return result;
}

private async Task<TildaRegistryEntry> GetOrganizationFromBR(string organizationNumber)
{
var brResultTask = Helpers.GetFromBR(organizationNumber, _erClient, false, _policyRegistry);
var accountsInformationTask = Helpers.GetAnnualTurnoverFromBR(organizationNumber, _client, _policyRegistry);
var brResultTask = await Helpers.GetFromBR(organizationNumber, _erClient, false, _policyRegistry);
var accountsInformationTask = await Helpers.GetAnnualTurnoverFromBR(organizationNumber, _client, _policyRegistry);

await Task.WhenAll(brResultTask, accountsInformationTask);
var kofuviAddresses = await Helpers.GetKofuviAddresses(_settings.KofuviEndpoint, organizationNumber, _kofuviClient, _logger);

var organization = await ConvertBRtoTilda(brResultTask.First(), accountsInformationTask);
if (kofuviAddresses.Count > 0)
{
organization.Emails = kofuviAddresses;
}

return await ConvertBRtoTilda(brResultTask.Result.First(), accountsInformationTask.Result);
return organization;
}

private async Task<List<EvidenceValue>> GetEvidenceValuesTilsynskoordinering(EvidenceHarvesterRequest req, TildaParameters param)
Expand Down
55 changes: 53 additions & 2 deletions src/Dan.Plugin.Tilda/Utils/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ private static async Task<List<BREntityRegisterEntry>> GetAllUnitsFromBR(string
string rawResult;
try
{
var response = await client.GetAsync($"http://data.brreg.no/enhetsregisteret/api/enheter/?overordnetEnhet={organizationNumber}");
var response = await client.GetAsync($"https://data.brreg.no/enhetsregisteret/api/enheter/?overordnetEnhet={organizationNumber}");
if (response.StatusCode == HttpStatusCode.NotFound)
{
throw new EvidenceSourcePermanentClientException(
Expand Down Expand Up @@ -208,7 +208,7 @@ public static async Task<List<BREntityRegisterEntry>> GetFromBR(string organizat
{
List<BREntityRegisterEntry> result = new List<BREntityRegisterEntry>();

if (organization == "111111111")
if (organization is "111111111" or "811105562")
{
result.Add(new BREntityRegisterEntry()
{
Expand Down Expand Up @@ -560,5 +560,56 @@ public static async Task<byte[]> GetPdfreport(string url, string sourceOrgNo, Ht

return result;
}

// Returns empty list on any error
public static async Task<List<string>> GetKofuviAddresses(string baseEndpoint, string organizationNumber, HttpClient client, ILogger logger)
{
var targetUrl = $"{baseEndpoint}/api/varslingsadresser/{organizationNumber}";
string responseString;
try
{
var response = await client.GetAsync(targetUrl);
if (!response.IsSuccessStatusCode)
{
logger.LogError(
"Failed to get kofuvi addresses for org={organizationNumber} on url={targetUrl} with unsuccessful response status={statusCode}",
organizationNumber, targetUrl, response.StatusCode
);
return new List<string>();
}
responseString = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
logger.LogError(
"Failed to get kofuvi addresses for org={organizationNumber} on url={targetUrl} with exception ex={ex} message={message} status={status}",
organizationNumber, targetUrl, ex.GetType().Name, ex.Message, "hardfail"
);
return new List<string>();
}

try
{
var kofuviResponse = JsonConvert.DeserializeObject<KofuviResponse>(responseString);
var addresses = kofuviResponse.Embedded.Notification.NotificationAddresses;
if (addresses is null || addresses.Count == 0)
{
return new List<string>();
}

return addresses
.Select(a => a.ContactInformation?.DigitalNotificationInformation?.NotificationEmail?.CompleteEmail)
.Where(a => a is not null)
.ToList();
}
catch (Exception ex)
{
logger.LogError(
"Failed to deserialize kofuvi response for org={organizationNumber} with exception ex={ex} message={message} status={status}",
organizationNumber, ex.GetType().Name, ex.Message, "hardfail"
);
return new List<string>();
}
}
}
}

0 comments on commit 83671e5

Please sign in to comment.