├── renovate.json
├── src
└── Altinn-application-owner-system
│ ├── Functions
│ ├── Properties
│ │ ├── serviceDependencies.json
│ │ └── serviceDependencies.local.json
│ ├── host.json
│ ├── Services
│ │ ├── Interface
│ │ │ ├── IPlatform.cs
│ │ │ ├── IAuthenticationClientWrapper.cs
│ │ │ ├── IMaskinPortenClientWrapper.cs
│ │ │ ├── IKeyVaultService.cs
│ │ │ ├── IAuthenticationService.cs
│ │ │ ├── IStorage.cs
│ │ │ ├── ISubscription.cs
│ │ │ ├── IQueueService.cs
│ │ │ └── IAltinnApp.cs
│ │ └── Implementation
│ │ │ ├── KeyVaultService.cs
│ │ │ ├── PlatformService.cs
│ │ │ ├── AuthenticationClientWrapper.cs
│ │ │ ├── MaskinPortenClientWrapper.cs
│ │ │ ├── SubscriptionService.cs
│ │ │ ├── QueueService.cs
│ │ │ ├── StorageService.cs
│ │ │ ├── AltinnAppService.cs
│ │ │ └── AuthenticationService.cs
│ ├── Config
│ │ ├── KeyVaultSettings.cs
│ │ ├── QueueStorageSettings.cs
│ │ └── AltinnApplicationOwnerSystemSettings.cs
│ ├── Models
│ │ ├── PushQueueReceipt.cs
│ │ ├── CloudEvent.cs
│ │ └── Subscription.cs
│ ├── TelemetryInitializer.cs
│ ├── EventsConfirmation.cs
│ ├── EventsReceiver.cs
│ ├── EventsFeedback.cs
│ ├── EventsSubscriber.cs
│ ├── EventsDeSubscriber.cs
│ ├── Program.cs
│ ├── Externsions
│ │ └── HttpClientExtension.cs
│ ├── EventsProcessor.cs
│ ├── Functions.csproj
│ ├── .gitignore
│ ├── Altinn3.ruleset
│ ├── Settings.StyleCop
│ └── AltinnApplicationsOwnerSystemFunctions.xml
│ └── Altinn-application-owner-system.sln
├── LICENSE
├── README.md
├── deployment
└── provision_application_owner_system.ps1
└── .gitignore
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "local>Altinn/renovate-config"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Properties/serviceDependencies.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "appInsights1": {
4 | "type": "appInsights"
5 | },
6 | "storage1": {
7 | "type": "storage",
8 | "connectionId": "AzureWebJobsStorage"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Properties/serviceDependencies.local.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "appInsights1": {
4 | "type": "appInsights.sdk"
5 | },
6 | "storage1": {
7 | "type": "storage.emulator",
8 | "connectionId": "AzureWebJobsStorage"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | },
10 | "logLevel": {
11 | "default": "Information",
12 | "Function": "Debug"
13 | }
14 | },
15 | "extensions": {
16 | "queues": {
17 | "maxPollingInterval": "00:00:02",
18 | "visibilityTimeout": "00:00:30",
19 | "maxDequeueCount": 5
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IPlatform.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 |
5 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
6 | {
7 | ///
8 | /// Interface for data handling
9 | ///
10 | public interface IPlatform
11 | {
12 | ///
13 | /// Gets the data as is.
14 | ///
15 | /// Uri to the data
16 | Task GetBinaryData(string dataUri);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Config/KeyVaultSettings.cs:
--------------------------------------------------------------------------------
1 | namespace AltinnApplicationOwnerSystem.Functions.Config
2 | {
3 | ///
4 | /// Configuration object used to hold settings for the KeyVault.
5 | ///
6 | public class KeyVaultSettings
7 | {
8 | ///
9 | /// Uri to keyvault
10 | ///
11 | public string KeyVaultURI { get; set; }
12 |
13 | ///
14 | /// Name of the certificate secret
15 | ///
16 | public string MaskinPortenCertSecretId { get; set; } = "maskinportenclientcert";
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IAuthenticationClientWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
4 | {
5 | ///
6 | /// Interface that defines the Authentication service responsible for converting MaskinPorten token to AltinnToken
7 | ///
8 | public interface IAuthenticationClientWrapper
9 | {
10 | ///
11 | /// Converts MaskinPortenToken to AltinnToken
12 | ///
13 | /// A new AltinnToken
14 | Task ConvertToken(string maskinportenToken);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IMaskinPortenClientWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
8 | {
9 | ///
10 | /// Interface defining the MaskinPorten Client wrapper responsible for authentication of Application Owner System
11 | ///
12 | public interface IMaskinPortenClientWrapper
13 | {
14 | ///
15 | /// Post MaskinPorten Authentication request
16 | ///
17 | Task PostToken(FormUrlEncodedContent bearer);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Models/PushQueueReceipt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace AltinnApplicationOwnerSystem.Functions.Models
7 | {
8 | ///
9 | /// Object to hold the receipt for a push queue action.
10 | ///
11 | public class PushQueueReceipt
12 | {
13 | ///
14 | /// Boolean to indicate if the push was successful.
15 | ///
16 | public bool Success { get; set; }
17 |
18 | ///
19 | /// Exception. Only populated if the push failed.
20 | ///
21 | public Exception Exception { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IKeyVaultService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
4 | {
5 | ///
6 | /// Interface for interacting with key vault
7 | ///
8 | public interface IKeyVaultService
9 | {
10 | ///
11 | /// Gets the value of a secret from the given key vault.
12 | ///
13 | /// The URI of the key vault to ask for secret.
14 | /// The id of the secret.
15 | /// The secret value.
16 | Task GetCertificateAsync(string vaultUri, string secretId);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/TelemetryInitializer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.ApplicationInsights.Channel;
2 | using Microsoft.ApplicationInsights.Extensibility;
3 |
4 | namespace AltinnIntegrator.Functions
5 | {
6 | ///
7 | /// Class that handles initialization of App Insights telemetry.
8 | ///
9 | public class TelemetryInitializer : ITelemetryInitializer
10 | {
11 | ///
12 | /// Initializer.
13 | ///
14 | /// The telemetry.
15 | public void Initialize(ITelemetry telemetry)
16 | {
17 | // set custom role name here
18 | telemetry.Context.Cloud.RoleName = "altinnintegrator-function";
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IAuthenticationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 |
6 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
7 | {
8 | ///
9 | /// Interface defining service responsible for the authentication process for Application Owner system
10 | ///
11 | public interface IAuthenticationService
12 | {
13 | ///
14 | /// Methods that return Altinn token. If not cached it will log the solution
15 | /// in to MaskinPorten and then exchange the Maskinporten token to an Altinn token.
16 | ///
17 | ///
18 | Task GetAltinnToken();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Config/QueueStorageSettings.cs:
--------------------------------------------------------------------------------
1 | namespace AltinnApplicationOwnerSystem.Functions.Config
2 | {
3 | ///
4 | /// Configuration object used to hold settings for the queue storage.
5 | ///
6 | public class QueueStorageSettings
7 | {
8 | ///
9 | /// ConnectionString for the storage account
10 | ///
11 | public string ConnectionString { get; set; }
12 |
13 | ///
14 | /// Name of the queue to push incomming events to.
15 | ///
16 | public string InboundQueueName { get; set; } = "events-inbound";
17 |
18 | ///
19 | /// Name of the queue to push confirmation
20 | ///
21 | public string ConfirmationQueueName { get; set; } = "events-confirmation";
22 |
23 | ///
24 | /// Name of the queue to push feedback
25 | ///
26 | public string FeedbackQueueName { get; set; } = "events-feedback";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IStorage.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading.Tasks;
3 |
4 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
5 | {
6 | ///
7 | /// Interface for Storage where Application Owner system store received data
8 | ///
9 | public interface IStorage
10 | {
11 | ///
12 | /// Saves a specific blob
13 | ///
14 | Task SaveBlob(string name, string data);
15 |
16 | ///
17 | /// Saves a stream to blob
18 | ///
19 | Task UploadFromStreamAsync(string name, Stream stream);
20 |
21 | ///
22 | /// Save registered subscription information
23 | ///
24 | Task SaveRegisteredSubscription(string name, Subscription subscription);
25 |
26 | ///
27 | /// Delete blob from container
28 | ///
29 | Task DeleteBlobFromContainer(string name, string container);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Altinn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Altinn-application-owner-system.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30717.126
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions", "Functions\Functions.csproj", "{40908F4C-13D8-41CA-B978-C951BB440729}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {40908F4C-13D8-41CA-B978-C951BB440729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {40908F4C-13D8-41CA-B978-C951BB440729}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {40908F4C-13D8-41CA-B978-C951BB440729}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {40908F4C-13D8-41CA-B978-C951BB440729}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {A0041B5D-CACD-4BC7-A851-B006712DACA7}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/ISubscription.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
4 | {
5 | ///
6 | /// Interface for managing event subscriptions
7 | ///
8 | public interface ISubscription
9 | {
10 | ///
11 | /// Register a new subscription
12 | ///
13 | /// Name of the blob
14 | /// The subscription to register
15 | /// The registered subscription
16 | Task RegisterSubscription(string name, Subscription subscription);
17 |
18 | ///
19 | /// Get a subscription by id
20 | ///
21 | /// The id of the subscription
22 | /// The subscription
23 | Task GetSubscription(string subscriptionId);
24 |
25 | ///
26 | /// Delete a subscription by id
27 | ///
28 | /// Name of the blob
29 | /// The id of the subscription
30 | /// The deleted subscription
31 | Task RemoveSubscription(string name, int subscriptionId);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IQueueService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using AltinnApplicationOwnerSystem.Functions.Models;
3 |
4 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
5 | {
6 | ///
7 | /// Interface to interact with the different queues used by the functions
8 | ///
9 | public interface IQueueService
10 | {
11 | ///
12 | /// Pushes the provided content to the queue
13 | ///
14 | /// The content to push to the queue in string format
15 | /// Returns a queue receipt
16 | Task PushToInboundQueue(string content);
17 |
18 | ///
19 | /// Pushes the provided content to the queue
20 | ///
21 | /// The content to push to the queue in string format
22 | /// Returns a queue receipt
23 | Task PushToConfirmationQueue(string content);
24 |
25 | ///
26 | /// Pushes the provided content to the feedback queue
27 | ///
28 | /// The content to push to the queue in string format
29 | /// Returns a queue receipt
30 | Task PushToFeedbackQueue(string content);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Interface/IAltinnApp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Altinn.Platform.Storage.Interface.Models;
5 |
6 | namespace AltinnApplicationOwnerSystem.Functions.Services.Interface
7 | {
8 | ///
9 | /// Interface for handling form data related operations
10 | ///
11 | public interface IAltinnApp
12 | {
13 | ///
14 | /// Gets the instance metadata for a given app
15 | ///
16 | Task GetInstance(string appId, string instanceId);
17 |
18 | ///
19 | /// Add complete confirmation.
20 | ///
21 | ///
22 | /// Add to an instance that a given stakeholder considers the instance as no longer needed by them. The stakeholder has
23 | /// collected all the data and information they needed from the instance and expect no additional data to be added to it.
24 | /// The body of the request isn't used for anything despite this being a POST operation.
25 | ///
26 | Task AddCompleteConfirmation(string instanceUri);
27 |
28 | ///
29 | /// Add feedback.
30 | ///
31 | ///
32 | /// Add feedback to an instance.
33 | ///
34 | Task AddXMLFeedback(string instanceUri, string instanceId, string eventType);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsConfirmation.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Threading.Tasks;
3 | using AltinnApplicationOwnerSystem.Functions.Models;
4 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
5 | using Microsoft.Azure.Functions.Worker;
6 |
7 | namespace AltinnApplicationOwnerSystem.Functions
8 | {
9 | ///
10 | /// Azure Function that confirmes that data for a given instance is downloaded
11 | ///
12 | public class EventsConfirmation
13 | {
14 | private readonly IAltinnApp _altinnApp;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | public EventsConfirmation(IAltinnApp altinnApp)
20 | {
21 | _altinnApp = altinnApp;
22 | }
23 |
24 | ///
25 | /// Function method that is triggered by new element on events-confirmation queue
26 | ///
27 | /// A representing the result of the asynchronous operation.
28 | [Function("EventsConfirmation")]
29 | public async Task Run([QueueTrigger("events-confirmation", Connection = "QueueStorageSettings:ConnectionString")]string item, FunctionContext executionContext)
30 | {
31 | CloudEvent cloudEvent = JsonSerializer.Deserialize(item);
32 |
33 | await _altinnApp.AddCompleteConfirmation(cloudEvent.Source.AbsoluteUri);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsReceiver.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Net;
3 | using System.Text.Json;
4 | using System.Threading.Tasks;
5 | using AltinnApplicationOwnerSystem.Functions.Config;
6 | using AltinnApplicationOwnerSystem.Functions.Models;
7 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
8 | using Microsoft.Azure.Functions.Worker;
9 | using Microsoft.Azure.Functions.Worker.Http;
10 | using Microsoft.Extensions.Options;
11 |
12 | namespace AltinnApplicationOwnerSystem.Functions
13 | {
14 | ///
15 | /// This function is responsible for receving events from Altinn Events.
16 | /// It will store events in the incomming que for processing by the EventsProcessor
17 | ///
18 | public class EventsReceiver
19 | {
20 | private readonly IQueueService _queueService;
21 |
22 | private readonly AltinnApplicationOwnerSystemSettings _settings;
23 |
24 | ///
25 | /// Initializes a new instance of the class.
26 | ///
27 | public EventsReceiver(IQueueService queueSerice, IOptions altinnIntegratorSettings)
28 | {
29 | _settings = altinnIntegratorSettings.Value;
30 | _queueService = queueSerice;
31 | }
32 |
33 | ///
34 | /// Webhook method to receive CloudEvents from Altinn Platform Events
35 | ///
36 | [Function("EventsReceiver")]
37 | public async Task Run(
38 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestData req,
39 | FunctionContext executionContext)
40 | {
41 | CloudEvent cloudEvent = await JsonSerializer.DeserializeAsync(req.Body);
42 |
43 | await _queueService.PushToInboundQueue(JsonSerializer.Serialize(cloudEvent));
44 |
45 | string responseMessage = "Cloud Event received and pushed to processing queue";
46 |
47 | HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
48 | response.WriteString(responseMessage);
49 | return response;
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/KeyVaultService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Threading.Tasks;
4 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
5 | using Azure;
6 | using Azure.Identity;
7 | using Azure.Security.KeyVault.Certificates;
8 | using Azure.Security.KeyVault.Secrets;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
12 | {
13 | ///
14 | /// Wrapper implementation for a KeyVaultClient. The wrapped client is created with a principal obtained through configuration.
15 | ///
16 | /// This class is excluded from code coverage because it has no logic to be tested.
17 | [ExcludeFromCodeCoverage]
18 | public class KeyVaultService : IKeyVaultService
19 | {
20 | private readonly ILogger _logger;
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | public KeyVaultService(ILogger logger)
26 | {
27 | _logger = logger;
28 | }
29 |
30 | ///
31 | public async Task GetCertificateAsync(string vaultUri, string secretId)
32 | {
33 | CertificateClient certificateClient = new CertificateClient(new Uri(vaultUri), new DefaultAzureCredential());
34 | AsyncPageable certificatePropertiesPage = certificateClient.GetPropertiesOfCertificateVersionsAsync(secretId);
35 | await foreach (CertificateProperties certificateProperties in certificatePropertiesPage)
36 | {
37 | if (certificateProperties.Enabled == true &&
38 | (certificateProperties.ExpiresOn == null || certificateProperties.ExpiresOn >= DateTime.UtcNow))
39 | {
40 | SecretClient secretClient = new SecretClient(new Uri(vaultUri), new DefaultAzureCredential());
41 |
42 | KeyVaultSecret secret = await secretClient.GetSecretAsync(certificateProperties.Name, certificateProperties.Version);
43 | return secret.Value;
44 | }
45 | }
46 |
47 | return null;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/PlatformService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Threading.Tasks;
7 | using AltinnApplicationOwnerSystem.Functions.Config;
8 | using AltinnApplicationOwnerSystem.Functions.Extensions;
9 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
10 | using Microsoft.Extensions.Options;
11 |
12 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
13 | {
14 | ///
15 | /// Service that downloads data from platform
16 | ///
17 | public class PlatformService : IPlatform
18 | {
19 | private readonly HttpClient _client;
20 | private readonly AltinnApplicationOwnerSystemSettings _settings;
21 | private readonly IAuthenticationService _authenticationService;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | public PlatformService(
27 | IOptions altinnIntegratorSettings,
28 | HttpClient httpClient,
29 | IAuthenticationService authenticationService)
30 | {
31 | _settings = altinnIntegratorSettings.Value;
32 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
33 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
34 | _client = httpClient;
35 | _authenticationService = authenticationService;
36 | }
37 |
38 | ///
39 | public async Task GetBinaryData(string dataUri)
40 | {
41 | string altinnToken = await _authenticationService.GetAltinnToken();
42 |
43 | HttpResponseMessage response = await _client.GetAsync(altinnToken, dataUri);
44 |
45 | if (response.IsSuccessStatusCode)
46 | {
47 | return await response.Content.ReadAsStreamAsync();
48 | }
49 | else if (response.StatusCode == HttpStatusCode.NotFound)
50 | {
51 | return null;
52 | }
53 |
54 | throw new ApplicationException();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/AuthenticationClientWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Net.Http.Headers;
3 | using System.Text.Json;
4 | using System.Threading.Tasks;
5 | using AltinnApplicationOwnerSystem.Functions.Config;
6 | using AltinnApplicationOwnerSystem.Functions.Extensions;
7 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
8 | using Microsoft.Extensions.Options;
9 |
10 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
11 | {
12 | ///
13 | /// HttpClient wrapper responsible for calling Altinn Platform Authentication to convert MaskinPorten token to AltinnToken
14 | ///
15 | public class AuthenticationClientWrapper : IAuthenticationClientWrapper
16 | {
17 | private readonly HttpClient _client;
18 |
19 | private readonly AltinnApplicationOwnerSystemSettings _settings;
20 |
21 | ///
22 | /// Gets or sets the base address
23 | ///
24 | private string BaseAddress { get; set; }
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | public AuthenticationClientWrapper(IOptions altinnIntegratorSettings, HttpClient httpClient)
30 | {
31 | _settings = altinnIntegratorSettings.Value;
32 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
33 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
34 | _client = httpClient;
35 | }
36 |
37 | ///
38 | public async Task ConvertToken(string token)
39 | {
40 | string cmd = $@"{_settings.PlatformBaseUrl}authentication/api/v1/exchange/maskinporten?test={_settings.TestMode}";
41 | HttpResponseMessage response = await _client.GetAsync(token, cmd);
42 |
43 | if (response.IsSuccessStatusCode)
44 | {
45 | string jsonString = await response.Content.ReadAsStringAsync();
46 | return JsonSerializer.Deserialize(jsonString);
47 | }
48 | else
49 | {
50 | return $@"Could not retrieve Altinn Token";
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsFeedback.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json;
3 | using System.Threading.Tasks;
4 | using AltinnApplicationOwnerSystem.Functions.Config;
5 | using AltinnApplicationOwnerSystem.Functions.Models;
6 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
7 | using Microsoft.Azure.Functions.Worker;
8 |
9 | namespace AltinnApplicationOwnerSystem.Functions
10 | {
11 | ///
12 | /// Azure Function that confirmes that data for a given instance is downloaded
13 | ///
14 | public class EventsFeedback
15 | {
16 | private readonly IAltinnApp _altinnApp;
17 |
18 | private readonly IQueueService _queueService;
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | public EventsFeedback(IAltinnApp altinnApp, IQueueService queueService)
24 | {
25 | _altinnApp = altinnApp;
26 | _queueService = queueService;
27 | }
28 |
29 | ///
30 | /// Function method that is triggered by new element on events-feedback queue
31 | ///
32 | /// A representing the result of the asynchronous operation.
33 | [Function("EventsFeedback")]
34 | public async Task Run([QueueTrigger("events-feedback", Connection = "QueueStorageSettings:ConnectionString")] string item, FunctionContext executionContext)
35 | {
36 | CloudEvent cloudEvent = JsonSerializer.Deserialize(item);
37 | (string appId, string instanceId) appInfo = GetInstanceInfoFromSource(cloudEvent.Source);
38 |
39 | await _altinnApp.AddXMLFeedback(cloudEvent.Source.AbsoluteUri, appInfo.instanceId, cloudEvent.Type);
40 |
41 | // Push to confirmation queue when Feedback is completed
42 | await _queueService.PushToConfirmationQueue(JsonSerializer.Serialize(cloudEvent));
43 | }
44 |
45 | ///
46 | /// Creates an instance for a given event
47 | ///
48 | private (string, string) GetInstanceInfoFromSource(Uri eventUri)
49 | {
50 | string[] parts = eventUri.Segments;
51 | (string appId, string instanceId) appInfo = ($"{parts[1]}{parts[2]}", $"{parts[4]}{parts[5]}");
52 | return appInfo;
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsSubscriber.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Threading.Tasks;
3 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
4 | using Microsoft.Azure.Functions.Worker;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace AltinnApplicationOwnerSystem.Functions
8 | {
9 | ///
10 | /// Azure Function that confirmes that data for a given instance is downloaded
11 | ///
12 | public class EventsSubscriber
13 | {
14 | private readonly ISubscription _subscriptionService;
15 |
16 | private static ILogger _logger;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | public EventsSubscriber(ISubscription subscriptionService, ILogger logger)
22 | {
23 | _subscriptionService = subscriptionService;
24 | _logger = logger;
25 | }
26 |
27 | ///
28 | /// Function method that register a new subscription when file is added to blob storage
29 | ///
30 | /// A representing the result of the asynchronous operationŒ.
31 | [Function("EventsSubscriber")]
32 | public async Task Run(
33 | [BlobTrigger("add-subscriptions/{name}.json", Connection = "QueueStorageSettings:ConnectionString")] byte[] blob, // Use byte[] https://github.com/Azure/azure-functions-dotnet-worker/issues/398
34 | string name)
35 | {
36 | string blobContent = System.Text.Encoding.UTF8.GetString(blob);
37 | _logger.LogInformation($"C# Blob trigger function Processed blob Name: {name} Content: {blobContent}");
38 | try
39 | {
40 | Subscription subscription = JsonSerializer.Deserialize(blobContent);
41 | _logger.LogInformation($"Deserialized object as json: {subscription.ToJson()}");
42 | await _subscriptionService.RegisterSubscription(name, subscription);
43 | }
44 | catch (JsonException ex)
45 | {
46 | _logger.LogError(ex, "Failed to deserialize blob content");
47 | }
48 | catch (System.Exception ex)
49 | {
50 | _logger.LogError(ex, "Error while registering subscription");
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsDeSubscriber.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Threading.Tasks;
3 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
4 | using Microsoft.Azure.Functions.Worker;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace AltinnApplicationOwnerSystem.Functions
8 | {
9 | ///
10 | /// Azure Function that confirmes that data for a given instance is downloaded
11 | ///
12 | public class EventsDeSubscriber
13 | {
14 | private readonly ISubscription _subscriptionService;
15 |
16 | private static ILogger _logger;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | public EventsDeSubscriber(ISubscription subscriptionService, ILogger logger)
22 | {
23 | _subscriptionService = subscriptionService;
24 | _logger = logger;
25 | }
26 |
27 | ///
28 | /// Function method that removes event subscription when file is added to blob storage
29 | ///
30 | /// A representing the result of the asynchronous operationŒ.
31 | [Function("EventsDeSubscriber")]
32 | public async Task Run(
33 | [BlobTrigger("remove-subscriptions/{name}.json", Connection = "QueueStorageSettings:ConnectionString")] byte[] blob, // Use byte[] https://github.com/Azure/azure-functions-dotnet-worker/issues/398
34 | string name)
35 | {
36 | string blobContent = System.Text.Encoding.UTF8.GetString(blob);
37 | _logger.LogInformation($"C# Blob trigger function Processed blob Name: {name} Content: {blobContent}");
38 | try
39 | {
40 | Subscription subscription = JsonSerializer.Deserialize(blobContent);
41 | _logger.LogInformation($"Deserialized object as json: {subscription.ToJson()}");
42 | await _subscriptionService.RemoveSubscription(name, subscription.Id);
43 | }
44 | catch (JsonException ex)
45 | {
46 | _logger.LogError(ex, "Failed to deserialize blob content");
47 | }
48 | catch (System.Exception ex)
49 | {
50 | _logger.LogError(ex, "Error while registering subscription");
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Program.cs:
--------------------------------------------------------------------------------
1 | using AltinnApplicationOwnerSystem.Functions.Config;
2 | using AltinnApplicationOwnerSystem.Functions.Services.Implementation;
3 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 |
8 | namespace AltinnApplicationsOwnerSystem.Functions
9 | {
10 | ///
11 | /// Host program for Azure Function
12 | ///
13 | public class Program
14 | {
15 | ///
16 | /// Main
17 | ///
18 | public static void Main()
19 | {
20 | var host = new HostBuilder()
21 | .ConfigureFunctionsWorkerDefaults()
22 | .ConfigureServices(s =>
23 | {
24 | s.AddOptions()
25 | .Configure((settings, configuration) =>
26 | {
27 | configuration.GetSection("AltinnApplicationOwnerSystemSettings").Bind(settings);
28 | });
29 | s.AddOptions()
30 | .Configure((settings, configuration) =>
31 | {
32 | configuration.GetSection("KeyVault").Bind(settings);
33 | });
34 |
35 | s.AddOptions()
36 | .Configure((settings, configuration) =>
37 | {
38 | configuration.GetSection("QueueStorageSettings").Bind(settings);
39 | });
40 |
41 | s.AddTransient();
42 | s.AddSingleton();
43 | s.AddSingleton();
44 | s.AddSingleton();
45 | s.AddHttpClient();
46 | s.AddHttpClient();
47 | s.AddHttpClient();
48 | s.AddHttpClient();
49 | s.AddHttpClient();
50 | })
51 | .Build();
52 |
53 | host.Run();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/MaskinPortenClientWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Net.Http.Headers;
4 | using System.Threading.Tasks;
5 | using AltinnApplicationOwnerSystem.Functions.Config;
6 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
7 | using Microsoft.Extensions.Logging;
8 | using Microsoft.Extensions.Options;
9 |
10 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
11 | {
12 | ///
13 | /// HttpClient wrapper responsible for calling the MaskinPorten endpont to authenticate the Application Owner System
14 | ///
15 | public class MaskinportenClientWrapper : IMaskinPortenClientWrapper
16 | {
17 | ///
18 | /// Application logger
19 | ///
20 | private readonly AltinnApplicationOwnerSystemSettings _settings;
21 |
22 | private readonly HttpClient _client;
23 |
24 | private static ILogger _logger;
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | public MaskinportenClientWrapper(IOptions altinnIntegratorSettings, HttpClient httpClient, ILogger logger)
30 | {
31 | _settings = altinnIntegratorSettings.Value;
32 | httpClient.BaseAddress = new Uri(_settings.MaskinportenBaseAddress);
33 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
34 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
35 | _client = httpClient;
36 | _logger = logger;
37 | }
38 |
39 | ///
40 | /// Gets or sets the base address
41 | ///
42 | private string BaseAddress { get; set; }
43 |
44 | ///
45 | public async Task PostToken(FormUrlEncodedContent bearer)
46 | {
47 | string token = string.Empty;
48 |
49 | HttpRequestMessage requestMessage = new HttpRequestMessage()
50 | {
51 | Method = HttpMethod.Post,
52 | RequestUri = new Uri(_settings.MaskinportenBaseAddress + "/token"),
53 | Content = bearer
54 | };
55 |
56 | HttpResponseMessage response = await _client.SendAsync(requestMessage);
57 |
58 | if (response.IsSuccessStatusCode)
59 | {
60 | token = await response.Content.ReadAsStringAsync();
61 | return token;
62 | }
63 | else
64 | {
65 | string error = await response.Content.ReadAsStringAsync();
66 | _logger.LogError(@"Could not retrieve Token" + error);
67 | }
68 |
69 | return null;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Config/AltinnApplicationOwnerSystemSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AltinnApplicationOwnerSystem.Functions.Config
6 | {
7 | ///
8 | /// Required configuration for the system
9 | ///
10 | public class AltinnApplicationOwnerSystemSettings
11 | {
12 | ///
13 | /// Base URL for the Apps cluster for the Application Owner this system is set up for. Example for TTD in TT02. https://ttd.apps.tt02.altinn.no
14 | ///
15 | public string AppsBaseUrl { get; set; }
16 |
17 | ///
18 | /// Base URL for the Platform cluster for the environment this ApplicationOwnerSystem is configured for Example: https://platform.tt02.altinn.no/
19 | ///
20 | public string PlatformBaseUrl { get; set; }
21 |
22 | ///
23 | /// Endpoint to blob storage
24 | ///
25 | public string BlobEndpoint { get; set; }
26 |
27 | ///
28 | /// Account name for Azure Storage Account
29 | ///
30 | public string AccountName { get; set; }
31 |
32 | ///
33 | /// The account key for the Azure Storage Account
34 | ///
35 | public string AccountKey { get; set; }
36 |
37 | ///
38 | /// The blob container to store incomming data
39 | ///
40 | public string StorageContainer { get; set; } = "inbound";
41 |
42 | ///
43 | /// The blob container where registered subscription info are stored
44 | ///
45 | public string RegisteredSubStorageContainer { get; set; } = "active-subscriptions";
46 |
47 | ///
48 | /// The base adress for Maskinporten in the environment where this Application Owner System is used Example: https://ver2.maskinporten.no
49 | ///
50 | public string MaskinportenBaseAddress { get; set; }
51 |
52 | ///
53 | /// The Application Owners Client ID
54 | ///
55 | public string MaskinPortenClientId { get; set; }
56 |
57 | ///
58 | /// Testmode. Only relevant for TTD and DIGDIR
59 | ///
60 | public bool TestMode { get; set; }
61 |
62 | ///
63 | /// Thumbprint for when running functions locally
64 | ///
65 | public string LocalCertThumbprint { get; set; }
66 |
67 | ///
68 | /// Datatype for xml feedback. If not set no feedback is generated
69 | ///
70 | public string XMLFeedbackDataType { get; set; } = string.Empty;
71 |
72 | ///
73 | /// Eventtype to generate xml feedback for
74 | ///
75 | public string XMLFeedbackEventType { get; set; } = string.Empty;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Externsions/HttpClientExtension.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace AltinnApplicationOwnerSystem.Functions.Extensions
6 | {
7 | ///
8 | /// This extension is created to make it easy to add a bearer token to a HttpRequests.
9 | ///
10 | public static class HttpClientExtension
11 | {
12 | ///
13 | /// Extension that add authorization header to request
14 | ///
15 | /// A HttpResponseMessage
16 | public static Task PostAsync(this HttpClient httpClient, string authorizationToken, string requestUri, HttpContent content)
17 | {
18 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
19 | request.Headers.Add("Authorization", "Bearer " + authorizationToken);
20 | request.Content = content;
21 | return httpClient.SendAsync(request, CancellationToken.None);
22 | }
23 |
24 | ///
25 | /// Extension that add authorization header to request
26 | ///
27 | /// A HttpResponseMessage
28 | public static Task PutAsync(this HttpClient httpClient, string authorizationToken, string requestUri, HttpContent content)
29 | {
30 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, requestUri);
31 | request.Headers.Add("Authorization", "Bearer " + authorizationToken);
32 | request.Content = content;
33 | return httpClient.SendAsync(request, CancellationToken.None);
34 | }
35 |
36 | ///
37 | /// Extension that add authorization header to request
38 | ///
39 | /// The HttpClient
40 | /// the authorization token (jwt)
41 | /// The request Uri
42 | /// A HttpResponseMessage
43 | public static Task GetAsync(this HttpClient httpClient, string authorizationToken, string requestUri)
44 | {
45 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
46 | request.Headers.Add("Authorization", "Bearer " + authorizationToken);
47 | return httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
48 | }
49 |
50 | ///
51 | /// Extension that add authorization header to request
52 | ///
53 | /// The HttpClient
54 | /// the authorization token (jwt)
55 | /// The request Uri
56 | /// A HttpResponseMessage
57 | public static Task DeleteAsync(this HttpClient httpClient, string authorizationToken, string requestUri)
58 | {
59 | HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Delete, requestUri);
60 | request.Headers.Add("Authorization", "Bearer " + authorizationToken);
61 | return httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Models/CloudEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Mime;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace AltinnApplicationOwnerSystem.Functions.Models
7 | {
8 | ///
9 | /// Represents a cloud event. Based on CloudEvent: https://github.com/cloudevents/spec/blob/v1.0/spec.md.
10 | ///
11 | public class CloudEvent
12 | {
13 | ///
14 | /// Gets or sets the id of the event.
15 | ///
16 | [JsonPropertyName("id")]
17 | public string Id { get; set; }
18 |
19 | ///
20 | /// Gets or sets the source of the event.
21 | ///
22 | [JsonPropertyName("source")]
23 | public Uri Source { get; set; }
24 |
25 | ///
26 | /// Gets or sets the specification version of the event.
27 | ///
28 | [JsonPropertyName("specversion")]
29 | public string SpecVersion { get; set; }
30 |
31 | ///
32 | /// Gets or sets the type of the event.
33 | ///
34 | [JsonPropertyName("type")]
35 | public string Type { get; set; }
36 |
37 | ///
38 | /// Gets or sets the subject of the event.
39 | ///
40 | [JsonPropertyName("subject")]
41 | public string Subject { get; set; }
42 |
43 | ///
44 | /// Gets or sets the time of the event.
45 | ///
46 | [JsonPropertyName("time")]
47 | public DateTime? Time { get; set; }
48 |
49 | ///
50 | /// Gets or sets the alternative subject of the event.
51 | ///
52 | [JsonPropertyName("alternativesubject")]
53 | public string AlternativeSubject { get; set; }
54 |
55 | ///
56 | /// Gets or sets the cloudEvent data content. The event payload.
57 | /// The payload depends on the type and the dataschema.
58 | ///
59 | [JsonPropertyName("data")]
60 | public object Data { get; set; }
61 |
62 | ///
63 | /// Gets or sets the cloudEvent dataschema attribute.
64 | /// A link to the schema that the data attribute adheres to.
65 | ///
66 | [JsonPropertyName("dataschema")]
67 | public Uri DataSchema { get; set; }
68 |
69 | ///
70 | /// Gets or sets the cloudEvent datacontenttype attribute.
71 | /// Content type of the data attribute value.
72 | ///
73 | [JsonPropertyName("contenttype")]
74 | public ContentType DataContentType { get; set; }
75 |
76 | ///
77 | /// Serializes the cloud event to a JSON string.
78 | ///
79 | /// Serialized cloud event
80 | public string Serialize()
81 | {
82 | return JsonSerializer.Serialize(this, new JsonSerializerOptions { IgnoreNullValues = true });
83 | }
84 |
85 | ///
86 | /// Deserializes the cloud event to a JSON string.
87 | ///
88 | /// Cloud event
89 | public static CloudEvent Deserialize(string jsonString)
90 | {
91 | return JsonSerializer.Deserialize(jsonString, new JsonSerializerOptions { });
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Models/Subscription.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace AltinnApplicationOwnerSystem.Functions
5 | {
6 | ///
7 | /// Subscription model
8 | ///
9 | public class Subscription
10 | {
11 | ///
12 | /// Gets or sets the subscription id
13 | ///
14 | [JsonPropertyName("id")]
15 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
16 | public int Id { get; set; }
17 |
18 | ///
19 | /// Gets or sets the subscription description
20 | ///
21 | [JsonPropertyName("description")]
22 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
23 | public string Description { get; set; }
24 |
25 | ///
26 | /// Gets or sets the subscription endpoint
27 | ///
28 | [JsonPropertyName("endPoint")]
29 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
30 | public string EndPoint { get; set; }
31 |
32 | ///
33 | /// Gets or sets the subscription sourceFilter
34 | ///
35 | [JsonPropertyName("sourceFilter")]
36 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
37 | public string SourceFilter { get; set; }
38 |
39 | ///
40 | /// Gets or sets the subscription subjectFilter
41 | ///
42 | [JsonPropertyName("subjectFilter")]
43 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
44 | public string SubjectFilter { get; set; }
45 |
46 | ///
47 | /// Gets or sets the subscription alternativeSubjectFilter
48 | ///
49 | [JsonPropertyName("alternativeSubjectFilter")]
50 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
51 | public string AlternativeSubjectFilter { get; set; }
52 |
53 | ///
54 | /// Gets or sets the subscription typeFilter
55 | ///
56 | [JsonPropertyName("typeFilter")]
57 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
58 | public string TypeFilter { get; set; }
59 |
60 | ///
61 | /// Gets or sets the subscription consumer
62 | ///
63 | [JsonPropertyName("consumer")]
64 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
65 | public string Consumer { get; set; }
66 |
67 | ///
68 | /// Gets or sets the subscription createdBy
69 | ///
70 | [JsonPropertyName("createdBy")]
71 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
72 | public string CreatedBy { get; set; }
73 |
74 | ///
75 | /// Gets or sets the subscription created
76 | ///
77 | [JsonPropertyName("created")]
78 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
79 | public string Created { get; set; }
80 |
81 | ///
82 | /// Deserializes the subscription from a JSON string.
83 | ///
84 | /// The JSON string to deserialize
85 | /// The deserialized subscription
86 | public static Subscription FromJson(string json)
87 | {
88 | return JsonSerializer.Deserialize(json);
89 | }
90 |
91 | ///
92 | /// Serializes the subscription to a JSON string.
93 | ///
94 | /// Serialized subscription
95 | public string ToJson()
96 | {
97 | return JsonSerializer.Serialize(this);
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/EventsProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text.Json;
4 | using System.Threading.Tasks;
5 | using Altinn.Platform.Storage.Interface.Models;
6 | using AltinnApplicationOwnerSystem.Functions.Models;
7 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
8 | using Microsoft.Azure.Functions.Worker;
9 | using Microsoft.Azure.WebJobs;
10 | using Microsoft.Extensions.Logging;
11 |
12 | namespace AltinnApplicationOwnerSystem.Functions
13 | {
14 | ///
15 | /// Azure Function responsible for downloading data for a given instance.
16 | /// Triggered by CloudEvent on Azure Queue
17 | /// When finished it forward CloudEvent to confirmation queue
18 | ///
19 | public class EventsProcessor
20 | {
21 | private readonly IAltinnApp _altinnApp;
22 |
23 | private readonly IPlatform _platform;
24 |
25 | private readonly IStorage _storage;
26 |
27 | private readonly IQueueService _queueService;
28 |
29 | private static ILogger _logger;
30 |
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | public EventsProcessor(
35 | IAltinnApp altinnApp,
36 | IPlatform platform,
37 | IStorage storage,
38 | IQueueService queueService,
39 | ILogger logger)
40 | {
41 | _altinnApp = altinnApp;
42 | _platform = platform;
43 | _storage = storage;
44 | _queueService = queueService;
45 | _logger = logger;
46 | }
47 |
48 | ///
49 | /// Reads cloud event from events-inbound queue and download instance and data for that given event and store it to configured azure storage
50 | ///
51 | [Function(nameof(EventsProcessor))]
52 | public async Task Run([Microsoft.Azure.Functions.Worker.QueueTrigger("events-inbound", Connection = "QueueStorageSettings:ConnectionString")] string item, FunctionContext executionContext)
53 | {
54 | CloudEvent cloudEvent = System.Text.Json.JsonSerializer.Deserialize(item);
55 |
56 | if (!ShouldProcessEvent(cloudEvent))
57 | {
58 | return;
59 | }
60 |
61 | (string appId, string instanceId) appInfo = GetInstanceInfoFromSource(cloudEvent.Source);
62 | Instance instance = await _altinnApp.GetInstance(appInfo.appId, appInfo.instanceId);
63 |
64 | string instanceGuid = instance.Id.Split("/")[1];
65 | string instanceFolder = instance.AppId + "/" + instanceGuid + "/";
66 | string instancePath = instanceFolder + instanceGuid;
67 | await _storage.SaveBlob(instancePath, JsonSerializer.Serialize(instance));
68 | foreach (DataElement data in instance.Data)
69 | {
70 | ResourceLinks links = data.SelfLinks;
71 | using (Stream stream = await _platform.GetBinaryData(links.Platform))
72 | {
73 | await _storage.UploadFromStreamAsync(data.BlobStoragePath, stream);
74 | }
75 | }
76 |
77 | await _queueService.PushToFeedbackQueue(JsonSerializer.Serialize(cloudEvent));
78 | }
79 |
80 | ///
81 | /// Creates an instance for a given event
82 | ///
83 | private (string, string) GetInstanceInfoFromSource(Uri eventUri)
84 | {
85 | string[] parts = eventUri.Segments;
86 | (string appId, string instanceId) appInfo = ($"{parts[1]}{parts[2]}", $"{parts[4]}{parts[5]}");
87 | return appInfo;
88 | }
89 |
90 | ///
91 | /// Will based on configuration decide if the event need to be processed. Todo add logic
92 | ///
93 | private bool ShouldProcessEvent(CloudEvent cloudEvent)
94 | {
95 | if (cloudEvent.Type == "platform.events.validatesubscription")
96 | {
97 | return false;
98 | }
99 |
100 | return true;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Functions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net5.0
4 | v3
5 | Exe
6 | AltinnApplicationsOwnerSystemFunctions
7 | AltinnApplicationsOwnerSystem.Functions
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | all
35 | runtime; build; native; contentfiles; analyzers; buildtransitive
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | PreserveNewest
44 |
45 |
46 | PreserveNewest
47 | Never
48 |
49 |
50 |
51 |
52 | all
53 | runtime; build; native; contentfiles; analyzers; buildtransitive
54 |
55 |
56 | settings.stylecop.json
57 |
58 |
59 |
60 | Altinn3.ruleset
61 |
62 |
63 | C:\repos\altinn-application-owner-system\src\Altinn-application-owner-system\Functions\AltinnApplicationsOwnerSystemFunctions.xml
64 |
65 |
66 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/SubscriptionService.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Http;
3 | using System.Net.Http.Headers;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using AltinnApplicationOwnerSystem.Functions.Config;
7 | using AltinnApplicationOwnerSystem.Functions.Extensions;
8 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.Extensions.Options;
11 |
12 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
13 | {
14 | ///
15 | /// SubscriptionService implements ISubscription and handles subscriptions against Altinn.
16 | ///
17 | public class SubscriptionService : ISubscription
18 | {
19 | private readonly ILogger _logger;
20 |
21 | private readonly HttpClient _client;
22 |
23 | private readonly AltinnApplicationOwnerSystemSettings _settings;
24 |
25 | private readonly IAuthenticationService _authenticationService;
26 |
27 | private readonly IStorage _storage;
28 |
29 | ///
30 | /// Create a instance of SubscriptionService
31 | ///
32 | public SubscriptionService(
33 | IOptions altinnIntegratorSettings,
34 | HttpClient httpClient,
35 | IAuthenticationService authenticationService,
36 | IStorage storage,
37 | ILogger logger)
38 | {
39 | _settings = altinnIntegratorSettings.Value;
40 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
41 | _client = httpClient;
42 | _authenticationService = authenticationService;
43 | _storage = storage;
44 | _logger = logger;
45 | }
46 |
47 | ///
48 | public async Task GetSubscription(string subscriptionId)
49 | {
50 | throw new System.NotImplementedException();
51 | }
52 |
53 | ///
54 | public async Task RegisterSubscription(string name, Subscription subscription)
55 | {
56 | string apiUrl = $"{_settings.PlatformBaseUrl}events/api/v1/subscriptions";
57 |
58 | string altinnToken = await _authenticationService.GetAltinnToken();
59 | HttpResponseMessage response = await _client.PostAsync(altinnToken, apiUrl, new StringContent(subscription.ToJson(), Encoding.UTF8, "application/json"));
60 | if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created)
61 | {
62 | string subscriptionPath = await response.Content.ReadAsStringAsync();
63 | _logger.LogInformation($"async: {subscriptionPath} Content: {response.Content} Reason: {response.ReasonPhrase}");
64 | Subscription registered = Subscription.FromJson(subscriptionPath);
65 | await _storage.SaveRegisteredSubscription($"{name}.json", registered);
66 | await _storage.DeleteBlobFromContainer($"{name}.json", "add-subscriptions");
67 | return registered;
68 | }
69 |
70 | _logger.LogError($"Failed to register subscription. Status code: {response.StatusCode} Phrase: {response.ReasonPhrase} Content: {response.Content}");
71 |
72 | return subscription;
73 | }
74 |
75 | ///
76 | public async Task RemoveSubscription(string name, int id)
77 | {
78 | string apiUrl = $"{_settings.PlatformBaseUrl}events/api/v1/subscriptions/{id}";
79 | string altinnToken = await _authenticationService.GetAltinnToken();
80 | HttpResponseMessage response = await _client.DeleteAsync(altinnToken, apiUrl);
81 | if (response.StatusCode == HttpStatusCode.OK)
82 | {
83 | await _storage.DeleteBlobFromContainer($"{name}.json", "active-subscriptions");
84 | await _storage.DeleteBlobFromContainer($"{name}.json", "remove-subscriptions");
85 | _logger.LogInformation("Subscription removed successfully");
86 | return;
87 | }
88 |
89 | _logger.LogError($"Failed to register subscription. Status code: {response.StatusCode}");
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/QueueService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using AltinnApplicationOwnerSystem.Functions.Config;
6 | using AltinnApplicationOwnerSystem.Functions.Models;
7 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
8 | using Azure.Storage.Queues;
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.Extensions.Options;
11 |
12 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
13 | {
14 | ///
15 | /// The queue service that handles actions related to the queue storage.
16 | ///
17 | [ExcludeFromCodeCoverage]
18 | public class QueueService : IQueueService
19 | {
20 | private readonly QueueStorageSettings _settings;
21 |
22 | private QueueClient _inboundQueueClient;
23 |
24 | private QueueClient _confirmationQueueClient;
25 |
26 | private QueueClient _feedbackQueueClient;
27 |
28 | private readonly ILogger _logger;
29 |
30 | ///
31 | /// Initializes a new instance of the class.
32 | ///
33 | public QueueService(IOptions settings, ILogger logger)
34 | {
35 | _settings = settings.Value;
36 | _logger = logger;
37 | }
38 |
39 | ///
40 | public async Task PushToInboundQueue(string content)
41 | {
42 | try
43 | {
44 | QueueClient client = await GetInboundQueueClient();
45 | await client.SendMessageAsync(Convert.ToBase64String(Encoding.UTF8.GetBytes(content)));
46 | }
47 | catch (Exception e)
48 | {
49 | return new PushQueueReceipt { Success = false, Exception = e };
50 | }
51 |
52 | return new PushQueueReceipt { Success = true };
53 | }
54 |
55 | ///
56 | public async Task PushToConfirmationQueue(string content)
57 | {
58 | try
59 | {
60 | QueueClient client = await GetConfirmationQueueClient();
61 | await client.SendMessageAsync(Convert.ToBase64String(Encoding.UTF8.GetBytes(content)));
62 | }
63 | catch (Exception e)
64 | {
65 | return new PushQueueReceipt { Success = false, Exception = e };
66 | }
67 |
68 | return new PushQueueReceipt { Success = true };
69 | }
70 |
71 | ///
72 | public async Task PushToFeedbackQueue(string content)
73 | {
74 | try
75 | {
76 | QueueClient client = await GetFeedbackQueueClient();
77 | await client.SendMessageAsync(Convert.ToBase64String(Encoding.UTF8.GetBytes(content)));
78 | }
79 | catch (Exception e)
80 | {
81 | return new PushQueueReceipt { Success = false, Exception = e };
82 | }
83 |
84 | return new PushQueueReceipt { Success = true };
85 | }
86 |
87 | private async Task GetInboundQueueClient()
88 | {
89 | if (_inboundQueueClient == null)
90 | {
91 | _inboundQueueClient = new QueueClient(_settings.ConnectionString, _settings.InboundQueueName);
92 | await _inboundQueueClient.CreateIfNotExistsAsync();
93 | }
94 |
95 | return _inboundQueueClient;
96 | }
97 |
98 | private async Task GetConfirmationQueueClient()
99 | {
100 | if (_confirmationQueueClient == null)
101 | {
102 | _confirmationQueueClient = new QueueClient(_settings.ConnectionString, _settings.ConfirmationQueueName);
103 | await _confirmationQueueClient.CreateIfNotExistsAsync();
104 | }
105 |
106 | return _confirmationQueueClient;
107 | }
108 |
109 | private async Task GetFeedbackQueueClient()
110 | {
111 | if (_feedbackQueueClient == null)
112 | {
113 | _feedbackQueueClient = new QueueClient(_settings.ConnectionString, _settings.FeedbackQueueName);
114 | await _feedbackQueueClient.CreateIfNotExistsAsync();
115 | }
116 |
117 | return _feedbackQueueClient;
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/StorageService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using AltinnApplicationOwnerSystem.Functions.Config;
5 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
6 | using Azure.Storage;
7 | using Azure.Storage.Blobs;
8 | using Azure.Storage.Blobs.Models;
9 | using Microsoft.Extensions.Options;
10 |
11 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
12 | {
13 | ///
14 | /// Class that handles integration with Azure Blob Storage.
15 | ///
16 | public class StorageService : IStorage
17 | {
18 | private readonly AltinnApplicationOwnerSystemSettings _settings;
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | public StorageService(IOptions altinnIntegratorSettings)
24 | {
25 | _settings = altinnIntegratorSettings.Value;
26 | }
27 |
28 | ///
29 | public async Task DeleteBlobFromContainer(string name, string container)
30 | {
31 | BlobClient client;
32 |
33 | StorageSharedKeyCredential storageCredentials = new StorageSharedKeyCredential(_settings.AccountName, _settings.AccountKey);
34 | BlobServiceClient serviceClient = new BlobServiceClient(new Uri(_settings.BlobEndpoint), storageCredentials);
35 | BlobContainerClient blobContainerClient = serviceClient.GetBlobContainerClient(container);
36 | client = blobContainerClient.GetBlobClient(name);
37 |
38 | await client.DeleteIfExistsAsync();
39 | }
40 |
41 | ///
42 | /// Saves data in blob storage defined in configuration.
43 | ///
44 | public async Task SaveBlob(string name, string data)
45 | {
46 | BlobClient client;
47 |
48 | StorageSharedKeyCredential storageCredentials = new StorageSharedKeyCredential(_settings.AccountName, _settings.AccountKey);
49 | BlobServiceClient serviceClient = new BlobServiceClient(new Uri(_settings.BlobEndpoint), storageCredentials);
50 | BlobContainerClient blobContainerClient = serviceClient.GetBlobContainerClient(_settings.StorageContainer);
51 |
52 | client = blobContainerClient.GetBlobClient(name);
53 |
54 | Stream stream = new MemoryStream();
55 | StreamWriter writer = new StreamWriter(stream);
56 | writer.Write(data);
57 | writer.Flush();
58 | stream.Position = 0;
59 | await client.UploadAsync(stream, true);
60 | stream.Dispose();
61 | }
62 |
63 | ///
64 | public async Task SaveRegisteredSubscription(string name, Subscription subscription)
65 | {
66 | BlobClient client;
67 |
68 | StorageSharedKeyCredential storageCredentials = new StorageSharedKeyCredential(_settings.AccountName, _settings.AccountKey);
69 | BlobServiceClient serviceClient = new BlobServiceClient(new Uri(_settings.BlobEndpoint), storageCredentials);
70 | BlobContainerClient blobContainerClient = serviceClient.GetBlobContainerClient(_settings.RegisteredSubStorageContainer);
71 |
72 | client = blobContainerClient.GetBlobClient(name);
73 |
74 | Stream stream = new MemoryStream();
75 | StreamWriter writer = new StreamWriter(stream);
76 | writer.Write(subscription.ToJson());
77 | writer.Flush();
78 | stream.Position = 0;
79 | await client.UploadAsync(stream, true);
80 | stream.Dispose();
81 | }
82 |
83 | ///
84 | public async Task UploadFromStreamAsync(string name, Stream stream)
85 | {
86 | BlobClient blockBlob = CreateBlobClient(name);
87 |
88 | await blockBlob.UploadAsync(stream, true);
89 | BlobProperties properties = await blockBlob.GetPropertiesAsync();
90 |
91 | return properties.ContentLength;
92 | }
93 |
94 | private BlobClient CreateBlobClient(string blobName)
95 | {
96 | BlobClient client;
97 |
98 | StorageSharedKeyCredential storageCredentials = new StorageSharedKeyCredential(_settings.AccountName, _settings.AccountKey);
99 | BlobServiceClient serviceClient = new BlobServiceClient(new Uri(_settings.BlobEndpoint), storageCredentials);
100 | BlobContainerClient blobContainerClient = serviceClient.GetBlobContainerClient(_settings.StorageContainer);
101 |
102 | client = blobContainerClient.GetBlobClient(blobName);
103 |
104 | return client;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Application Owner System
2 | A reference implementation of a system for app owners, that can react to events, fetch data and update app status.
3 |
4 | ## Architecture
5 |
6 | The system is implemented as Azure Functions using .Net 5
7 |
8 | - It uses Azure Functions to receive, process, and confirm downloading of data
9 | - It uses Azure Queue Storage for async processing
10 | - It uses Azure Blob Storage to store data.
11 | - It uses Azure KeyVault to store the certificate for [MaskinPorten](https://www.digdir.no/digitale-felleslosninger/maskinporten/869)
12 |
13 | 
14 |
15 | ### Events Receiver
16 |
17 | The events receiver is a webhook that receives events from Altinn Events.
18 |
19 | It will put the received Event in a Queue based on Azure Queue Storage.
20 |
21 | The function is protected by a [Function Access Key](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=csharp#authorization-keys). This needs to be present in URI in requests from Altinn events.
22 |
23 | The endpoint for subscription needs to include it.
24 | Example: https://aos-ttdt22-function.azurewebsites.net/api/eventsreceiver?code=ffQqMrbvLoNEiySae0EfApmost8LfBeqdYY/AXa13KSyf8Rjsp1U9w==
25 |
26 | Azure creates the code when setting up the functions.
27 |
28 | ### Events Processor
29 |
30 | Events processor is responsible to
31 |
32 | - Download Instance document
33 | - Download all data and store it as blobs in an Azure Storage Account
34 |
35 | Put events on the feedback queue
36 |
37 | ### Events Feedback
38 |
39 | Events feedback is responsible for
40 |
41 | - Upload xml feedback to a datafield controlled by the setting: AltinnApplicationOwnerSystemSettings:XMLFeedbackDataType
42 | - Put events on the confirmation queue
43 |
44 | In addition to configure the XMLFeedbackDataType you also need to specify the type of events that should trigger upload of feedback. Controlled with the setting: AltinnApplicationOwnerSystemSettings:XMLFeedbackEventType
45 |
46 | ### Events Confirmation
47 |
48 | - Calls App to confirm that data is downloaded (and feedback uploaded if configured)
49 |
50 | ### Events Subscriber
51 |
52 | Events subscriber is triggered when a file is uploaded to the blob container add-subscriptions
53 |
54 | Events subscriber is responsible for:
55 |
56 | - Authenticate with maskinporten to altinn3 and register the subscription uploaded
57 | - Saving the response from altinn3 to the blob container active-subscriptions
58 | - Deleting the uploaded file in add-subscriptions
59 |
60 | ### Events DeSubscriber
61 |
62 | Events desubscriber is triggered when a file is uploaded to the blob container: remove-subscriptions
63 |
64 | The simplest way to get the json to upload is to download it form the blob container: active-subscriptions
65 |
66 | Events Desubscriber is responsible for:
67 |
68 | - Authenticate with maskinporten to altinn3 and remove the subscription uploaded
69 | - Removing the file describing the subscription in active-subscriptions
70 | - Deleteing the uploaded file in remove-subscriptions
71 |
72 | ## Authentication
73 |
74 | The system uses Maskinporten to authenticate the application owner.
75 |
76 | - The org needs to be registered as a client
77 |
78 | ## Configuration
79 |
80 | As part of this project, you find a PowerShell script to deploy the solution to an Azure Subscription
81 |
82 | ### Prerequisites
83 |
84 | - You have an Azure subscription that you can log in to and create resources
85 | - Org is registred with a client in MaskinPorten and you have the clientId
86 | - You have the certificate for that client with password
87 | - The client is an application owner in Altinn
88 |
89 | From deployment folder run in Powershell. Replace values matching your environment
90 | Example: #
91 |
92 | ```bash
93 | .\provision_application_owner_system.ps1 -subscription Altinn-TTD-Application-Owner-System -aosEnvironment [INSERT NAME ON ENVIRONMENT MAX 5 letters] -maskinportenclient [INSERT MASKINPORTEN CLIENTID] -maskinportenclientcert [PATH TO CERT] -maskinportenclientcertpwd [INSERT PASSOWORD FOR CERT] -maskinportenuri https://ver2.maskinporten.no -platformuri https://platform.tt02.altinn.no/ -appsuri https://ttd.apps.tt02.altinn.no/
94 | ```
95 |
96 |
97 | ### Setting up a subscription
98 |
99 | Below you see and example on how an subscription is set up. This need to be posted to the subscription endpoint while authenticated as org.
100 |
101 | See more details on [Subscription API](https://docs.altinn.studio/teknologi/altinnstudio/altinn-api/platform-api/events/)
102 |
103 | ```json
104 | {
105 | "endPoint": "https://aos-ttdtt02-function.azurewebsites.net/api/eventsreceiver?code=swEizasdgraeadvcvvwFAKE8y9ZS7bDoZNOTREALLuPeJMkUtHow==",
106 | "sourceFilter": "https://ttd.apps.tt02.altinn.no/ttd/apps-test",
107 | "typeFilter": "app.instance.process.completed",
108 | }
109 |
110 | ```
111 |
112 | ### Altinn 3 documentation
113 |
114 | 
115 |
116 | Here you find relevant information.
117 | - [Altinn 3 documentation](https://docs.altinn.studio/technology/)
118 | - [Altinn 3 API](https://docs.altinn.studio/api/)
119 | - [Altinn 3 Event capabilities](https://docs.altinn.studio/technology/architecture/capabilities/runtime/integration/events/)
120 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Azure Functions localsettings file
5 | local.settings.json
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # MSTest test Results
34 | [Tt]est[Rr]esult*/
35 | [Bb]uild[Ll]og.*
36 |
37 | # NUNIT
38 | *.VisualState.xml
39 | TestResult.xml
40 |
41 | # Build Results of an ATL Project
42 | [Dd]ebugPS/
43 | [Rr]eleasePS/
44 | dlldata.c
45 |
46 | # DNX
47 | project.lock.json
48 | project.fragment.lock.json
49 | artifacts/
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | #*.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.jfm
196 | *.pfx
197 | *.publishsettings
198 | node_modules/
199 | orleans.codegen.cs
200 |
201 | # Since there are multiple workflows, uncomment next line to ignore bower_components
202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
203 | #bower_components/
204 |
205 | # RIA/Silverlight projects
206 | Generated_Code/
207 |
208 | # Backup & report files from converting an old project file
209 | # to a newer Visual Studio version. Backup files are not needed,
210 | # because we have git ;-)
211 | _UpgradeReport_Files/
212 | Backup*/
213 | UpgradeLog*.XML
214 | UpgradeLog*.htm
215 |
216 | # SQL Server files
217 | *.mdf
218 | *.ldf
219 |
220 | # Business Intelligence projects
221 | *.rdl.data
222 | *.bim.layout
223 | *.bim_*.settings
224 |
225 | # Microsoft Fakes
226 | FakesAssemblies/
227 |
228 | # GhostDoc plugin setting file
229 | *.GhostDoc.xml
230 |
231 | # Node.js Tools for Visual Studio
232 | .ntvs_analysis.dat
233 |
234 | # Visual Studio 6 build log
235 | *.plg
236 |
237 | # Visual Studio 6 workspace options file
238 | *.opt
239 |
240 | # Visual Studio LightSwitch build output
241 | **/*.HTMLClient/GeneratedArtifacts
242 | **/*.DesktopClient/GeneratedArtifacts
243 | **/*.DesktopClient/ModelManifest.xml
244 | **/*.Server/GeneratedArtifacts
245 | **/*.Server/ModelManifest.xml
246 | _Pvt_Extensions
247 |
248 | # Paket dependency manager
249 | .paket/paket.exe
250 | paket-files/
251 |
252 | # FAKE - F# Make
253 | .fake/
254 |
255 | # JetBrains Rider
256 | .idea/
257 | *.sln.iml
258 |
259 | # CodeRush
260 | .cr/
261 |
262 | # Python Tools for Visual Studio (PTVS)
263 | __pycache__/
264 | *.pyc
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/AltinnAppService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Net.Http.Headers;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Altinn.Platform.Storage.Interface.Models;
8 | using AltinnApplicationOwnerSystem.Functions.Config;
9 | using AltinnApplicationOwnerSystem.Functions.Extensions;
10 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
11 | using Microsoft.Extensions.Logging;
12 | using Microsoft.Extensions.Options;
13 |
14 | using Newtonsoft.Json;
15 |
16 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
17 | {
18 | ///
19 | /// App implementation of the instance service that talks to the given app
20 | ///
21 | public class AltinnAppService : IAltinnApp
22 | {
23 | private readonly ILogger _logger;
24 |
25 | private readonly HttpClient _client;
26 |
27 | private readonly AltinnApplicationOwnerSystemSettings _settings;
28 |
29 | private readonly IAuthenticationService _authenticationService;
30 |
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | public AltinnAppService(
35 | IOptions altinnIntegratorSettings,
36 | HttpClient httpClient,
37 | IAuthenticationService authenticationService,
38 | ILogger logger)
39 | {
40 | _settings = altinnIntegratorSettings.Value;
41 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
42 | httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
43 | _client = httpClient;
44 | _authenticationService = authenticationService;
45 | _logger = logger;
46 | }
47 |
48 | ///
49 | public async Task GetInstance(string appId, string instanceId)
50 | {
51 | string apiUrl = $"{_settings.AppsBaseUrl}{appId}instances/{instanceId}";
52 |
53 | string altinnToken = await _authenticationService.GetAltinnToken();
54 |
55 | HttpResponseMessage response = await _client.GetAsync(altinnToken, apiUrl);
56 | if (response.StatusCode == HttpStatusCode.OK)
57 | {
58 | string instanceData = await response.Content.ReadAsStringAsync();
59 | Instance instance = JsonConvert.DeserializeObject(instanceData);
60 | return instance;
61 | }
62 | else
63 | {
64 | _logger.LogError($"Unable to fetch instance with instance id {instanceId} " + response.StatusCode + " " + apiUrl);
65 | throw new ApplicationException();
66 | }
67 | }
68 |
69 | ///
70 | public async Task AddCompleteConfirmation(string instanceUri)
71 | {
72 | string apiUrl = $"{instanceUri}/complete";
73 | string altinnToken = await _authenticationService.GetAltinnToken();
74 |
75 | HttpResponseMessage response = await _client.PostAsync(altinnToken, apiUrl, new StringContent(string.Empty));
76 |
77 | if (response.StatusCode == HttpStatusCode.OK)
78 | {
79 | string instanceData = await response.Content.ReadAsStringAsync();
80 | Instance instance = JsonConvert.DeserializeObject(instanceData);
81 | return instance;
82 | }
83 |
84 | throw new ApplicationException();
85 | }
86 |
87 | ///
88 | public async Task AddXMLFeedback(string instanceUri, string instanceId, string eventType)
89 | {
90 | string xmlFeedBackDataType = _settings.XMLFeedbackDataType;
91 | string feedbackOnEventType = _settings.XMLFeedbackEventType;
92 | if (!string.IsNullOrEmpty(xmlFeedBackDataType) && !string.IsNullOrEmpty(feedbackOnEventType) && feedbackOnEventType.Equals(eventType))
93 | {
94 | string apiUrl = $"{instanceUri}/data?dataType={xmlFeedBackDataType}";
95 | string altinnToken = await _authenticationService.GetAltinnToken();
96 | var contentData = new StringContent(GenerateFeedbackXML(instanceId), Encoding.UTF8, "text/xml");
97 | contentData.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
98 | contentData.Headers.TryAddWithoutValidation("Content-Disposition", "attachment; filename=test.xml");
99 | HttpResponseMessage response = await _client.PostAsync(altinnToken, apiUrl, contentData);
100 |
101 | if (response.StatusCode == HttpStatusCode.Created)
102 | {
103 | string instanceData = await response.Content.ReadAsStringAsync();
104 | Instance instance = JsonConvert.DeserializeObject(instanceData);
105 | return instance;
106 | }
107 | else
108 | {
109 | var responseContent = await response.Content.ReadAsStringAsync();
110 | _logger.LogError($"Unable to add xml feedback with instance id {instanceId} {response.StatusCode} message {response.ReasonPhrase} content {responseContent}");
111 | }
112 |
113 | throw new ApplicationException();
114 | }
115 |
116 | return new Instance();
117 | }
118 |
119 | private string GenerateFeedbackXML(string instanceId)
120 | {
121 | string xml = $"" +
122 | $"{instanceId}" +
123 | $"This is a feedback" +
124 | $"1" +
125 | $"{DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss")}" +
126 | $"aos-ttd" +
127 | $"";
128 | return xml;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/deployment/provision_application_owner_system.ps1:
--------------------------------------------------------------------------------
1 | ### PREREQUISITS
2 | # AZ CLI
3 | # HELM 3
4 | # AZ Login
5 | # azure-functions-core-tools https://www.npmjs.com/package/azure-functions-core-tools
6 | # az extension add -n application-insights #Add support for Application Insight for AzureCLI
7 | #
8 |
9 | ### Example usage for TTD in TT02
10 | # .\provision_application_owner_system.ps1 -subscription Altinn-TTD-Application-Owner-System -aosEnvironment [INSERT NAME ON ENVIRONMENT MAX 5 letters] -maskinportenclient [INSERT MASKINPORTEN CLIENTID] -maskinportenclientcert [PATH TO CERT] -maskinportenclientcertpwd [INSERT PASSOWORD FOR CERT] -maskinportenuri https://ver2.maskinporten.no -platformuri https://platform.tt02.altinn.no/ -appsuri https://ttd.apps.tt02.altinn.no/
11 |
12 |
13 | param (
14 | [Parameter(Mandatory=$True)][string]$subscription,
15 | [Parameter(Mandatory=$True)][string]$aosEnvironment,
16 | [Parameter(Mandatory=$True)][string]$maskinportenclient,
17 | [Parameter(Mandatory=$True)][string]$maskinportenclientcert,
18 | [Parameter(Mandatory=$True)][string]$maskinportenclientcertpwd,
19 | [Parameter(Mandatory=$True)][string]$maskinportenuri,
20 | [Parameter(Mandatory=$True)][string]$appsuri,
21 | [Parameter(Mandatory=$True)][string]$platformuri,
22 | [string]$location = "norwayeast",
23 | [string]$resourcePrefix = "aos"
24 | )
25 |
26 | ### Set subscription
27 | az account set --subscription $subscription
28 |
29 | $aosResourceGroupName = "$resourcePrefix-$aosEnvironment-rg"
30 | $keyvaultname = "$resourcePrefix-$aosEnvironment-keyvault"
31 | $storageAccountName = $resourcePrefix+$aosEnvironment+"storage"
32 | $functionName = "$resourcePrefix-$aosEnvironment-function"
33 |
34 |
35 | #### Check if resource group for AKS exists
36 | $resourceGroupExists = az group exists --name $aosResourceGroupName
37 | if (![System.Convert]::ToBoolean($resourceGroupExists)) {
38 | az group create --location $location --name $aosResourceGroupName
39 | }
40 |
41 | Write-Output "Create Storage Account $storageAccountName"
42 | $storageAccount = az storage account create -n $storageAccountName -g $aosResourceGroupName -l $location --sku Standard_GRS --kind StorageV2 --access-tier Hot
43 | $StorageID = az storage account show --name $storageAccountName --resource-group $aosResourceGroupName --query id --output tsv
44 | $storageAccountAccountKey = az storage account keys list --account-name $storageAccountName --query [0].value -o tsv
45 | az storage container create -n inbound --account-name $storageAccountName --account-key $storageAccountAccountKey --public-access off
46 | az storage container create -n active-subscriptions --account-name $storageAccountName --account-key $storageAccountAccountKey --public-access off
47 | az storage container create -n add-subscriptions --account-name $storageAccountName --account-key $storageAccountAccountKey --public-access off
48 | az storage container create -n remove-subscriptions --account-name $storageAccountName --account-key $storageAccountAccountKey --public-access off
49 | $storageConnectionString = az storage account show-connection-string -g $aosResourceGroupName -n $storageAccountName --query connectionString --output tsv
50 | $blobendpoint = az storage account show --name $storageAccountName --resource-group $aosResourceGroupName --query primaryEndpoints.blob --output tsv
51 |
52 |
53 | Write-Output "Create KeyVault"
54 | $keyvault = az keyvault create --location $location --name $keyvaultname --resource-group $aosResourceGroupName
55 | $KeyvaultID = az keyvault show --name $keyvaultname --resource-group $aosResourceGroupName --query id --output tsv
56 | $vaultUri = az keyvault show --name $keyvaultname --resource-group $aosResourceGroupName --query properties.vaultUri --output tsv
57 |
58 |
59 | Write-Output "Import Maskinporten cert"
60 | az keyvault certificate import --vault-name $keyvaultname -n maskinportenclientcert -f $maskinportenclientcert --password $maskinportenclientcertpwd
61 |
62 | Write-Output "Create Function App"
63 | az functionapp create --resource-group $aosResourceGroupName --consumption-plan-location $location --runtime dotnet-isolated --functions-version 3 --name $functionName --storage-account $storageAccountName
64 | az functionapp identity assign -g $aosResourceGroupName -n $functionName
65 | $funcprincialId = az functionapp identity show --name $functionName --resource-group $aosResourceGroupName --query principalId --output tsv
66 |
67 | Write-Output "Set policy"
68 | az keyvault set-policy --name $keyvaultname --object-id $funcprincialId --secret-permissions get --certificate-permissions list
69 |
70 | Write-Output "Set config"
71 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:AccountKey=$storageAccountAccountKey"
72 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:AccountName=$storageAccountName"
73 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:AppsBaseUrl=$appsuri"
74 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:BlobEndpoint=$blobendpoint"
75 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "QueueStorageSettings:ConnectionString=$storageConnectionString"
76 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:PlatformBaseUrl=$platformuri"
77 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:MaskinportenBaseAddress=$maskinportenuri"
78 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:MaskinPortenClientId=$maskinportenclient"
79 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "AltinnApplicationOwnerSystemSettings:TestMode=true"
80 | az functionapp config appsettings set --name $functionname --resource-group $aosResourceGroupName --settings "KeyVault:KeyVaultURI=$vaultUri"
81 |
82 |
83 | Write-Output "Set config"
84 | Set-Location ..\src\Altinn-application-owner-system\Functions
85 | func azure functionapp publish $functionName
86 | Set-Location ..\..\..\deployment
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Services/Implementation/AuthenticationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IdentityModel.Tokens.Jwt;
4 | using System.Net.Http;
5 | using System.Security.Cryptography.X509Certificates;
6 | using System.Threading.Tasks;
7 | using AltinnApplicationOwnerSystem.Functions.Config;
8 | using AltinnApplicationOwnerSystem.Functions.Services.Interface;
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.Extensions.Options;
11 | using Microsoft.IdentityModel.Tokens;
12 | using Newtonsoft.Json;
13 | using Newtonsoft.Json.Linq;
14 |
15 | namespace AltinnApplicationOwnerSystem.Functions.Services.Implementation
16 | {
17 | ///
18 | /// Authentication service responsible for authenticate the application owner system in MaskinPorten and Exchange token to an Altinn token
19 | ///
20 | public class AuthenticationService : IAuthenticationService
21 | {
22 | private readonly KeyVaultSettings _keyVaultSettings;
23 | private readonly IKeyVaultService _keyVaultService;
24 | private readonly AltinnApplicationOwnerSystemSettings _altinnIntegratorSettings;
25 | private readonly IMaskinPortenClientWrapper _maskinportenClientWrapper;
26 | private readonly IAuthenticationClientWrapper _authenticationClientWrapper;
27 | private readonly ILogger _logger;
28 | private string _altinnToken;
29 | private DateTime _tokenTimeout;
30 |
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | public AuthenticationService(
35 | IKeyVaultService keyVaultService,
36 | IOptions keyVaultSettings,
37 | IOptions altinnIntegratorSettings,
38 | IMaskinPortenClientWrapper maskinPortenClientWrapper,
39 | IAuthenticationClientWrapper authenticationClientWrapper,
40 | ILogger logger)
41 | {
42 | _keyVaultSettings = keyVaultSettings.Value;
43 | _altinnIntegratorSettings = altinnIntegratorSettings.Value;
44 | _keyVaultService = keyVaultService;
45 | _maskinportenClientWrapper = maskinPortenClientWrapper;
46 | _authenticationClientWrapper = authenticationClientWrapper;
47 | _logger = logger;
48 | }
49 |
50 | ///
51 | /// Creates a altinn token. First login to MaskinPorten and then call altinn to convert to Altinn token.
52 | ///
53 | /// The altinn token
54 | public async Task GetAltinnToken()
55 | {
56 | // To reduce traffic we cache this.
57 | if (!string.IsNullOrEmpty(_altinnToken) && _tokenTimeout > DateTime.Now)
58 | {
59 | return _altinnToken;
60 | }
61 |
62 | string jwtAssertion = await GetJwtAssertion();
63 |
64 | FormUrlEncodedContent content = GetUrlEncodedContent(jwtAssertion);
65 | string maskinPortenToken = await _maskinportenClientWrapper.PostToken(content);
66 |
67 | if (!string.IsNullOrEmpty(maskinPortenToken))
68 | {
69 | var accessTokenObject = JsonConvert.DeserializeObject(maskinPortenToken);
70 |
71 | string altinnToken = await _authenticationClientWrapper.ConvertToken(accessTokenObject.GetValue("access_token").ToString());
72 |
73 | _altinnToken = altinnToken;
74 | _tokenTimeout = DateTime.Now.AddSeconds(9);
75 | return altinnToken;
76 | }
77 |
78 | return null;
79 | }
80 |
81 | private FormUrlEncodedContent GetUrlEncodedContent(string assertion)
82 | {
83 | FormUrlEncodedContent formContent = new FormUrlEncodedContent(new List>
84 | {
85 | new KeyValuePair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),
86 | new KeyValuePair("assertion", assertion),
87 | });
88 |
89 | return formContent;
90 | }
91 |
92 | ///
93 | /// Creates the JWT Assertion used to authenticate system in Maskinporten
94 | ///
95 | ///
96 | public async Task GetJwtAssertion()
97 | {
98 | DateTimeOffset dateTimeOffset = new DateTimeOffset(DateTime.UtcNow);
99 | Guid clientId = new Guid(_altinnIntegratorSettings.MaskinPortenClientId);
100 |
101 | X509Certificate2 cert = null;
102 | if (!string.IsNullOrEmpty(_altinnIntegratorSettings.LocalCertThumbprint))
103 | {
104 | cert = GetCertificateFromKeyStore(_altinnIntegratorSettings.LocalCertThumbprint, StoreName.My, StoreLocation.LocalMachine);
105 | }
106 | else
107 | {
108 | cert = await GetCertificateFromKeyVault();
109 | }
110 |
111 | X509SecurityKey securityKey = new X509SecurityKey(cert);
112 | JwtHeader header = new JwtHeader(new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256))
113 | {
114 | { "x5c", new List() { Convert.ToBase64String(cert.GetRawCertData()) } }
115 | };
116 | header.Remove("typ");
117 | header.Remove("kid");
118 |
119 | JwtPayload payload = new JwtPayload
120 | {
121 | { "aud", "https://ver2.maskinporten.no/" },
122 | { "resource", "https://tt02.altinn.no/maskinporten-api/" },
123 | { "scope", "altinn:serviceowner/instances.read altinn:serviceowner/instances.write" },
124 | { "iss", clientId },
125 | { "exp", dateTimeOffset.ToUnixTimeSeconds() + 10 },
126 | { "iat", dateTimeOffset.ToUnixTimeSeconds() },
127 | { "jti", Guid.NewGuid().ToString() },
128 | };
129 |
130 | JwtSecurityToken securityToken = new JwtSecurityToken(header, payload);
131 | JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
132 |
133 | return handler.WriteToken(securityToken);
134 | }
135 |
136 | private async Task GetCertificateFromKeyVault()
137 | {
138 | string certBase64 = await _keyVaultService.GetCertificateAsync(_keyVaultSettings.KeyVaultURI, _keyVaultSettings.MaskinPortenCertSecretId);
139 | return new X509Certificate2(Convert.FromBase64String(certBase64), (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
140 | }
141 |
142 | private static X509Certificate2 GetCertificateFromKeyStore(string thumbprint, StoreName storeName, StoreLocation storeLocation, bool onlyValid = false)
143 | {
144 | var store = new X509Store(storeName, storeLocation);
145 | store.Open(OpenFlags.ReadOnly);
146 | var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, onlyValid);
147 | var enumerator = certCollection.GetEnumerator();
148 | X509Certificate2 cert = null;
149 | while (enumerator.MoveNext())
150 | {
151 | cert = enumerator.Current;
152 | }
153 |
154 | return cert;
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Altinn3.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/Settings.StyleCop:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | preprocessor, pre-processor
5 | shortlived, short-lived
6 |
7 |
8 | altinn
9 | arbeidsgiveravgift
10 | aspx
11 | BankID
12 | brreg
13 | Buypass
14 | Commfides
15 | compat
16 | Compat.browser
17 | Creuna
18 | css
19 | dequeue
20 | Dequeue
21 | deserializing
22 | Determinator
23 | enum
24 | en-US
25 | formset
26 | Functoid
27 | ID-Porten
28 | js
29 | leveranse
30 | linq
31 | msdn
32 | oppgave
33 | orid
34 | participant
35 | Porten
36 | psa
37 | referer
38 | reportee
39 | sone
40 | ssn
41 | subform
42 | subforms
43 | virksomhet
44 | Winnovative
45 | xfd
46 | xsd
47 | Guid
48 | Api
49 | OAuth
50 | Auth
51 | mpcId
52 | mpc
53 | Sdp
54 | Difi
55 | Difis
56 | Rijndael
57 | eq
58 | orderby
59 | Oppgaveregister
60 | Seres
61 | reportees
62 |
63 | 10000
64 |
65 |
66 |
67 |
68 |
69 |
70 | False
71 |
72 |
73 |
74 |
75 | False
76 |
77 |
78 |
79 |
80 | False
81 |
82 |
83 |
84 |
85 | False
86 |
87 |
88 |
89 |
90 | False
91 |
92 |
93 |
94 |
95 | False
96 |
97 |
98 |
99 |
100 | False
101 |
102 |
103 |
104 |
105 | True
106 |
107 |
108 |
109 |
110 |
111 |
112 | False
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | False
123 |
124 |
125 |
126 |
127 | False
128 |
129 |
130 |
131 |
132 |
133 | a1
134 | as
135 | at
136 | d
137 | db
138 | dn
139 | do
140 | dr
141 | ds
142 | dt
143 | e
144 | e2
145 | er
146 | f
147 | fs
148 | go
149 | id
150 | if
151 | in
152 | ip
153 | is
154 | js
155 | li
156 | my
157 | no
158 | ns
159 | on
160 | or
161 | pi
162 | pv
163 | sa
164 | sb
165 | se
166 | si
167 | so
168 | sp
169 | tc
170 | to
171 | tr
172 | ui
173 | un
174 | wf
175 | ws
176 | x
177 | y
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | False
186 |
187 |
188 |
189 |
190 | False
191 |
192 |
193 |
194 |
195 | False
196 |
197 |
198 |
199 |
200 | False
201 |
202 |
203 |
204 |
205 | False
206 |
207 |
208 |
209 |
210 | False
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 | False
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | False
231 |
232 |
233 |
234 |
235 |
236 |
237 |
--------------------------------------------------------------------------------
/src/Altinn-application-owner-system/Functions/AltinnApplicationsOwnerSystemFunctions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AltinnApplicationsOwnerSystemFunctions
5 |
6 |
7 |
8 |
9 | Required configuration for the system
10 |
11 |
12 |
13 |
14 | Base URL for the Apps cluster for the Application Owner this system is set up for. Example for TTD in TT02. https://ttd.apps.tt02.altinn.no
15 |
16 |
17 |
18 |
19 | Base URL for the Platform cluster for the environment this ApplicationOwnerSystem is configured for Example: https://platform.tt02.altinn.no/
20 |
21 |
22 |
23 |
24 | Endpoint to blob storage
25 |
26 |
27 |
28 |
29 | Account name for Azure Storage Account
30 |
31 |
32 |
33 |
34 | The account key for the Azure Storage Account
35 |
36 |
37 |
38 |
39 | The blob container to store incomming data
40 |
41 |
42 |
43 |
44 | The base adress for Maskinporten in the environment where this Application Owner System is used Example: https://ver2.maskinporten.no
45 |
46 |
47 |
48 |
49 | The Application Owners Client ID
50 |
51 |
52 |
53 |
54 | Testmode. Only relevant for TTD and DIGDIR
55 |
56 |
57 |
58 |
59 | Thumbprint for when running functions locally
60 |
61 |
62 |
63 |
64 | Configuration object used to hold settings for the KeyVault.
65 |
66 |
67 |
68 |
69 | Uri to keyvault
70 |
71 |
72 |
73 |
74 | Name of the certificate secret
75 |
76 |
77 |
78 |
79 | Configuration object used to hold settings for the queue storage.
80 |
81 |
82 |
83 |
84 | ConnectionString for the storage account
85 |
86 |
87 |
88 |
89 | Name of the queue to push incomming events to.
90 |
91 |
92 |
93 |
94 | Name of the queue to push confirmation
95 |
96 |
97 |
98 |
99 | Azure Function that confirmes that data for a given instance is downloaded
100 |
101 |
102 |
103 |
104 | Initializes a new instance of the class.
105 |
106 |
107 |
108 |
109 | Function method that is triggered by new element on events-confirmation queue
110 |
111 | A representing the result of the asynchronous operation.
112 |
113 |
114 |
115 | Azure Function responsible for downloading data for a given instance.
116 | Triggered by CloudEvent on Azure Queue
117 | When finished it forward CloudEvent to confirmation queue
118 |
119 |
120 |
121 |
122 | Initializes a new instance of the class.
123 |
124 |
125 |
126 |
127 | Reads cloud event from events-inbound queue and download instance and data for that given event and store it to configured azure storage
128 |
129 |
130 |
131 |
132 | Creates an instance for a given event
133 |
134 |
135 |
136 |
137 | Will based on configuration decide if the event need to be processed. Todo add logic
138 |
139 |
140 |
141 |
142 | This function is responsible for receving events from Altinn Events.
143 | It will store events in the incomming que for processing by the EventsProcessor
144 |
145 |
146 |
147 |
148 | Initializes a new instance of the class.
149 |
150 |
151 |
152 |
153 | Webhook method to receive CloudEvents from Altinn Platform Events
154 |
155 |
156 |
157 |
158 | This extension is created to make it easy to add a bearer token to a HttpRequests.
159 |
160 |
161 |
162 |
163 | Extension that add authorization header to request
164 |
165 | A HttpResponseMessage
166 |
167 |
168 |
169 | Extension that add authorization header to request
170 |
171 | The HttpClient
172 | the authorization token (jwt)
173 | The request Uri
174 | A HttpResponseMessage
175 |
176 |
177 |
178 | Represents a cloud event. Based on CloudEvent: https://github.com/cloudevents/spec/blob/v1.0/spec.md.
179 |
180 |
181 |
182 |
183 | Gets or sets the id of the event.
184 |
185 |
186 |
187 |
188 | Gets or sets the source of the event.
189 |
190 |
191 |
192 |
193 | Gets or sets the specification version of the event.
194 |
195 |
196 |
197 |
198 | Gets or sets the type of the event.
199 |
200 |
201 |
202 |
203 | Gets or sets the subject of the event.
204 |
205 |
206 |
207 |
208 | Gets or sets the time of the event.
209 |
210 |
211 |
212 |
213 | Gets or sets the alternative subject of the event.
214 |
215 |
216 |
217 |
218 | Gets or sets the cloudEvent data content. The event payload.
219 | The payload depends on the type and the dataschema.
220 |
221 |
222 |
223 |
224 | Gets or sets the cloudEvent dataschema attribute.
225 | A link to the schema that the data attribute adheres to.
226 |
227 |
228 |
229 |
230 | Gets or sets the cloudEvent datacontenttype attribute.
231 | Content type of the data attribute value.
232 |
233 |
234 |
235 |
236 | Serializes the cloud event to a JSON string.
237 |
238 | Serialized cloud event
239 |
240 |
241 |
242 | Deserializes the cloud event to a JSON string.
243 |
244 | Cloud event
245 |
246 |
247 |
248 | Object to hold the receipt for a push queue action.
249 |
250 |
251 |
252 |
253 | Boolean to indicate if the push was successful.
254 |
255 |
256 |
257 |
258 | Exception. Only populated if the push failed.
259 |
260 |
261 |
262 |
263 | App implementation of the instance service that talks to the given app
264 |
265 |
266 |
267 |
268 | Initializes a new instance of the class.
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 | HttpClient wrapper responsible for calling Altinn Platform Authentication to convert MaskinPorten token to AltinnToken
280 |
281 |
282 |
283 |
284 | Gets or sets the base address
285 |
286 |
287 |
288 |
289 | Initializes a new instance of the class.
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 | Authentication service responsible for authenticate the application owner system in MaskinPorten and Exchange token to an Altinn token
298 |
299 |
300 |
301 |
302 | Initializes a new instance of the class.
303 |
304 |
305 |
306 |
307 | Creates a altinn token. First login to MaskinPorten and then call altinn to convert to Altinn token.
308 |
309 | The altinn token
310 |
311 |
312 |
313 | Creates the JWT Assertion used to authenticate system in Maskinporten
314 |
315 |
316 |
317 |
318 |
319 | Wrapper implementation for a KeyVaultClient. The wrapped client is created with a principal obtained through configuration.
320 |
321 | This class is excluded from code coverage because it has no logic to be tested.
322 |
323 |
324 |
325 | Initializes a new instance of the class.
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 | HttpClient wrapper responsible for calling the MaskinPorten endpont to authenticate the Application Owner System
334 |
335 |
336 |
337 |
338 | Application logger
339 |
340 |
341 |
342 |
343 | Initializes a new instance of the class.
344 |
345 |
346 |
347 |
348 | Gets or sets the base address
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 | Service that downloads data from platform
357 |
358 |
359 |
360 |
361 | Initializes a new instance of the class.
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 | The queue service that handles actions related to the queue storage.
370 |
371 |
372 |
373 |
374 | Initializes a new instance of the class.
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 | Class that handles integration with Azure Blob Storage.
386 |
387 |
388 |
389 |
390 | Initializes a new instance of the class.
391 |
392 |
393 |
394 |
395 | Saves data in blob storage defined in configuration.
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 | Interface for handling form data related operations
404 |
405 |
406 |
407 |
408 | Gets the instance metadata for a given app
409 |
410 |
411 |
412 |
413 | Add complete confirmation.
414 |
415 |
416 | Add to an instance that a given stakeholder considers the instance as no longer needed by them. The stakeholder has
417 | collected all the data and information they needed from the instance and expect no additional data to be added to it.
418 | The body of the request isn't used for anything despite this being a POST operation.
419 |
420 |
421 |
422 |
423 | Interface that defines the Authentication service responsible for converting MaskinPorten token to AltinnToken
424 |
425 |
426 |
427 |
428 | Converts MaskinPortenToken to AltinnToken
429 |
430 | A new AltinnToken
431 |
432 |
433 |
434 | Interface defining service responsible for the authentication process for Application Owner system
435 |
436 |
437 |
438 |
439 | Methods that return Altinn token. If not cached it will log the solution
440 | in to MaskinPorten and then exchange the Maskinporten token to an Altinn token.
441 |
442 |
443 |
444 |
445 |
446 | Interface for interacting with key vault
447 |
448 |
449 |
450 |
451 | Gets the value of a secret from the given key vault.
452 |
453 | The URI of the key vault to ask for secret.
454 | The id of the secret.
455 | The secret value.
456 |
457 |
458 |
459 | Interface defining the MaskinPorten Client wrapper responsible for authentication of Application Owner System
460 |
461 |
462 |
463 |
464 | Post MaskinPorten Authentication request
465 |
466 |
467 |
468 |
469 | Interface for data handling
470 |
471 |
472 |
473 |
474 | Gets the data as is.
475 |
476 | Uri to the data
477 |
478 |
479 |
480 | Interface to interact with the different queues used by the functions
481 |
482 |
483 |
484 |
485 | Pushes the provided content to the queue
486 |
487 | The content to push to the queue in string format
488 | Returns a queue receipt
489 |
490 |
491 |
492 | Pushes the provided content to the queue
493 |
494 | The content to push to the queue in string format
495 | Returns a queue receipt
496 |
497 |
498 |
499 | Interface for Storage where Application Owner system store received data
500 |
501 |
502 |
503 |
504 | Saves a specific blob
505 |
506 |
507 |
508 |
509 | Saves a stream to blob
510 |
511 |
512 |
513 |
514 | Host program for Azure Function
515 |
516 |
517 |
518 |
519 | Main
520 |
521 |
522 |
523 |
524 | Class that handles initialization of App Insights telemetry.
525 |
526 |
527 |
528 |
529 | Initializer.
530 |
531 | The telemetry.
532 |
533 |
534 |
535 |
--------------------------------------------------------------------------------