Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/signing delegation #982

Open
wants to merge 27 commits into
base: feature/signing
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ba20fe2
rename method
HauklandJ Dec 16, 2024
eecc009
add terlemetry to singing delegation
HauklandJ Dec 16, 2024
5520ae3
use instancemuitator instead of instance
HauklandJ Dec 16, 2024
90df6d7
lastname changes
HauklandJ Dec 17, 2024
47ad35f
format
HauklandJ Dec 17, 2024
9652e07
temp fix for bad model
HauklandJ Dec 17, 2024
f135fbd
add debug logging
HauklandJ Dec 18, 2024
db1a9f6
format
HauklandJ Dec 18, 2024
2b20a3c
domain model for application resource id
HauklandJ Dec 18, 2024
ff8fddf
format
HauklandJ Dec 18, 2024
c7bad6f
use app resource id
HauklandJ Dec 18, 2024
6776a0e
add AppResourceId and simplify parameters to signing delegation
HauklandJ Dec 18, 2024
9edd11f
debug logging
HauklandJ Dec 18, 2024
fbb57df
lets try loggin again
HauklandJ Dec 18, 2024
894a1a4
format
HauklandJ Dec 18, 2024
882ff62
update instantiation of AppIdentifier
HauklandJ Dec 18, 2024
575aed2
public
HauklandJ Dec 18, 2024
d0d23f5
use actual instance id
HauklandJ Dec 18, 2024
5859551
inject taskId as param, due to being unable to resolve it
HauklandJ Dec 19, 2024
c4b4ef2
add delegation builder test
HauklandJ Dec 19, 2024
a962424
better logging for access management issues
HauklandJ Dec 19, 2024
6d1c1e7
rm unused using
HauklandJ Dec 19, 2024
2ebb778
better logging
HauklandJ Dec 19, 2024
c8be137
just log everything
HauklandJ Dec 19, 2024
28cfc5b
just log everything but better
HauklandJ Dec 19, 2024
dc36bc0
forgot one
HauklandJ Dec 19, 2024
af3e79f
add summary for all public props
HauklandJ Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,17 @@
ct
);

await _signingService.ProcessSignees(cachedDataMutator, signeeContexts, signatureConfiguration, ct);
await _signingService.ProcessSignees(
currentTask.Id,
cachedDataMutator,
signeeContexts,
signatureConfiguration,
ct
);
var changes = cachedDataMutator.GetDataElementChanges(false);
await cachedDataMutator.SaveChanges(changes);

//TODO: Return failure result if something failed.

Check warning on line 106 in src/Altinn.App.Core/Features/Action/InitializeDelegatedSigningUserAction.cs

View workflow job for this annotation

GitHub Actions / Static code analysis

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

return UserActionResult.SuccessResult();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Altinn.App.Core.Features.Signing.Models;
using Altinn.Platform.Storage.Interface.Models;
using Altinn.App.Core.Models;

namespace Altinn.App.Core.Features.Signing.Interfaces;

internal interface ISigningDelegationService
{
internal Task<List<SigneeContext>> DelegateSigneeRights(
internal Task<(List<SigneeContext>, bool success)> DelegateSigneeRights(
string taskId,
Instance instance,
string instanceId,
string instanceOwnerPartyId,
AppIdentifier appIdentifier,
List<SigneeContext> signeeContexts,
CancellationToken ct
CancellationToken ct,
Telemetry? telemetry = null
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ CancellationToken ct
);

Task<List<SigneeContext>> ProcessSignees(
string taskId,
IInstanceDataMutator instanceMutator,
List<SigneeContext> signeeContexts,
AltinnSignatureConfiguration signatureConfiguration,
Expand Down
105 changes: 59 additions & 46 deletions src/Altinn.App.Core/Features/Signing/SigningDelegationService.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
using System.Globalization;
using Altinn.App.Core.Features.Signing.Interfaces;
using Altinn.App.Core.Features.Signing.Models;
using Altinn.App.Core.Helpers;
using Altinn.App.Core.Internal.AccessManagement;
using Altinn.App.Core.Internal.AccessManagement.Builders;
using Altinn.App.Core.Internal.AccessManagement.Models;
using Altinn.App.Core.Internal.AccessManagement.Models.Shared;
using Altinn.Platform.Storage.Interface.Models;
using Altinn.App.Core.Models;
using Microsoft.Extensions.Logging;
using static Altinn.App.Core.Features.Telemetry.DelegationConst;

namespace Altinn.App.Core.Features.Signing;

internal sealed class SigningDelegationService(IAccessManagementClient accessManagementClient)
: ISigningDelegationService
internal sealed class SigningDelegationService(
IAccessManagementClient accessManagementClient,
ILogger<SigningDelegationService> logger
) : ISigningDelegationService
{
public async Task<List<SigneeContext>> DelegateSigneeRights(
public async Task<(List<SigneeContext>, bool success)> DelegateSigneeRights(
string taskId,
Instance instance,
string instanceId,
string instanceOwnerPartyId,
AppIdentifier appIdentifier,
List<SigneeContext> signeeContexts,
CancellationToken ct
CancellationToken ct,
Telemetry? telemetry = null
)
{
logger.LogInformation($"------------------------------------------------------------------------");
var actualInstanceId = instanceId.Split("/")[1];
var appResourceId = AppResourceId.FromAppIdentifier(appIdentifier);
// log appIdentifier and appResourceId
logger.LogInformation($"AppIdentifier: {appIdentifier.Org}/{appIdentifier.App}");
logger.LogInformation($"AppResourceId: {appResourceId.Value}");
bool success = true;
logger.LogInformation($"------------------------------------------------------------------------");
logger.LogInformation($"Delegating signee rights for task {taskId}.");
foreach (SigneeContext signeeContext in signeeContexts)
{
SigneeState state = signeeContext.SigneeState;
Expand All @@ -28,53 +42,52 @@ CancellationToken ct
{
if (state.IsAccessDelegated is false)
{
DelegationRequest delegationRequest = DelegationBuilder
.Create()
.WithApplicationId(instance.AppId)
.WithInstanceId(instance.Id)
.WithDelegator(new Delegator { IdType = DelegationConst.Party, Id = "" })
.WithRecipient(
new Delegatee
var dr = new DelegationRequest
{
To = new Delegatee
{
Id = signeeContext.PartyId.ToString(CultureInfo.InvariantCulture),
IdType = DelegationConst.Party,
},
From = new Delegator { Id = instanceOwnerPartyId, IdType = DelegationConst.Party },
ResourceId = appResourceId.Value,
InstanceId = actualInstanceId,
Rights =
[
new RightRequest
{
IdType = DelegationConst.Party,
Id = signeeContext.PartyId.ToString(CultureInfo.InvariantCulture),
}
)
.WithRights(
[
AccessRightBuilder
.Create()
.WithAction(ActionType.Read)
.WithResources(
[
new Resource { Value = AppIdHelper.ToResourceId(instance.AppId) },
new Resource { Type = DelegationConst.Task, Value = taskId },
]
)
.Build(),
AccessRightBuilder
.Create()
.WithAction(ActionType.Sign)
.WithResources(
[
new Resource { Value = AppIdHelper.ToResourceId(instance.AppId) },
new Resource { Type = DelegationConst.Task, Value = taskId },
]
)
.Build(),
]
)
.Build();
var response = await accessManagementClient.DelegateRights(delegationRequest, ct);
Resource =
[
new Resource { Type = DelegationConst.Resource, Value = appResourceId.Value },
new Resource { Type = DelegationConst.Task, Value = taskId },
],
Action = new AltinnAction { Type = DelegationConst.ActionId, Value = "read" },
},
new RightRequest
{
Resource =
[
new Resource { Type = DelegationConst.Resource, Value = appResourceId.Value },
new Resource { Type = DelegationConst.Task, Value = taskId },
],
Action = new AltinnAction { Type = DelegationConst.ActionId, Value = "sign" },
},
],
};
var response = await accessManagementClient.DelegateRights(dr, ct);
state.IsAccessDelegated = await Task.FromResult(true);
telemetry?.RecordDelegation(DelegationResult.Success);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to delegate signee rights");
state.DelegationFailedReason = "Failed to delegate signee rights: " + ex.Message;
telemetry?.RecordDelegation(DelegationResult.Error);
success = false;
}
}

return signeeContexts;
return (signeeContexts, success);
}
}
38 changes: 24 additions & 14 deletions src/Altinn.App.Core/Features/Signing/SigningService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.Json;
using Altinn.App.Core.Features.Signing.Exceptions;
Expand All @@ -22,7 +23,7 @@ internal sealed class SigningService(
IPersonClient personClient,
IOrganizationClient organisationClient,
IAltinnPartyClient altinnPartyClient,
// ISigningDelegationService signingDelegationService,
ISigningDelegationService signingDelegationService,
// ISigningNotificationService signingNotificationService,
IEnumerable<ISigneeProvider> signeeProviders,
IDataClient dataClient,
Expand Down Expand Up @@ -84,20 +85,32 @@ CancellationToken ct
}

public async Task<List<SigneeContext>> ProcessSignees(
string taskId,
IInstanceDataMutator instanceMutator,
List<SigneeContext> signeeContexts,
AltinnSignatureConfiguration signatureConfiguration,
CancellationToken ct
)
{
using Activity? activity = telemetry?.StartAssignSigneesActivity();
string taskId = instanceMutator.Instance.Process.CurrentTask.ElementId;

// await signingDelegationService.DelegateSigneeRights(taskId, instanceMutator.Instance, signeeContexts, ct);

//TODO: If something fails inside DelegateSigneeRights, abort and don't send notifications. Set error state in SigneeState.
string instanceOwnerPartyId = instanceMutator.Instance.InstanceOwner.PartyId;
string instanceId = instanceMutator.Instance.Id;

AppIdentifier appIdentifier = new(instanceMutator.Instance.AppId);
(signeeContexts, var delegateSuccess) = await signingDelegationService.DelegateSigneeRights(
taskId,
instanceId,
instanceOwnerPartyId,
appIdentifier,
signeeContexts,
ct,
telemetry
);

// await signingNotificationService.NotifySignatureTask(signeeContexts, ct);
if (delegateSuccess)
{
// await signingNotificationService.NotifySignatureTask(signeeContexts, ct);
}

// ! TODO: Remove nullable
instanceMutator.AddBinaryDataElement(
Expand Down Expand Up @@ -202,19 +215,16 @@ CancellationToken ct
List<SigneeContext> personSigneeContexts = [];
foreach (PersonSignee personSignee in signeeResult.PersonSignees)
{
var lastName = personSignee.LastName.Split(" ").First().ToLower(CultureInfo.InvariantCulture);
_logger.LogInformation(
"Looking up person with SSN {SocialSecurityNumber} and last name {LastName}.",
personSignee.SocialSecurityNumber,
personSignee.LastName.Split(" ").Last()
lastName
);
Person? person =
await personClient.GetPerson(
personSignee.SocialSecurityNumber,
personSignee.LastName.Split(" ").Last(),
ct
)
await personClient.GetPerson(personSignee.SocialSecurityNumber, lastName, ct)
?? throw new SignaturePartyNotValidException(
$"The given SSN and last name did not match any person in the registry."
$"The given SSN: {personSignee.SocialSecurityNumber} and last name: {lastName} did not match any person in the registry."
);
Party? party = await altinnPartyClient.LookupParty(
new PartyLookup { Ssn = personSignee.SocialSecurityNumber }
Expand Down
44 changes: 44 additions & 0 deletions src/Altinn.App.Core/Features/Telemetry/Telemetry.SigningService.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using NetEscapades.EnumGenerators;
using static Altinn.App.Core.Features.Telemetry.DelegationConst;
using Tag = System.Collections.Generic.KeyValuePair<string, object?>;

namespace Altinn.App.Core.Features;

partial class Telemetry
{
/// <summary>
/// Prometheus' increase and rate functions do not register the first value as an increase, but rather as the registration.<br/>
/// This means that going from none (non-existant) to 1 on a counter will register as an increase of 0.<br/>
/// In order to workaround this, we initialize to 0 for all metrics here.<br/>
/// Github issue can be found <a href="https://github.com/prometheus/prometheus/issues/3806">here</a>.
/// </summary>
/// <param name="context"></param>
private void InitSigning(InitContext context)
{
InitMetricCounter(
context,
MetricNameDelegation,
init: static m =>
{
foreach (var result in DelegationResultExtensions.GetValues())
{
m.Add(0, new Tag(InternalLabels.Result, result.ToStringFast()));
}
}
);
}

internal void RecordDelegation(DelegationResult result) =>
_counters[MetricNameDelegation].Add(1, new Tag(InternalLabels.Result, result.ToStringFast()));

internal Activity? StartAssignSigneesActivity() => ActivitySource.StartActivity("SigningService.AssignSignees");

internal Activity? StartReadSigneesActivity() => ActivitySource.StartActivity("SigningService.ReadSignees");

internal Activity? StartSignActivity() => ActivitySource.StartActivity("SigningService.Sign"); // TODO: expand to include signee id

internal static class DelegationConst
{
internal static readonly string MetricNameDelegation = Metrics.CreateLibName("signing_delegations");

[EnumExtensions]
internal enum DelegationResult
{
[Display(Name = "success")]
Success,

[Display(Name = "error")]
Error,
}
}
}
1 change: 1 addition & 0 deletions src/Altinn.App.Core/Features/Telemetry/Telemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ internal void Init()
InitData(context);
InitInstances(context);
InitNotifications(context);
InitSigning(context);
InitProcesses(context);
InitValidation(context);
InitMaskinporten(context);
Expand Down
43 changes: 0 additions & 43 deletions src/Altinn.App.Core/Helpers/AppIdHelper.cs

This file was deleted.

Loading
Loading