Skip to content

Commit

Permalink
Add metadata to TRN requests (#1683)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad authored Nov 15, 2024
1 parent 714e65e commit 168fd8b
Show file tree
Hide file tree
Showing 35 changed files with 4,038 additions and 309 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ public static void Main(string[] args)
services.AddSingleton<ICurrentUserProvider, ClaimsPrincipalCurrentUserProvider>();
services.AddMemoryCache();
services.AddSingleton<AddTrnToSentryScopeResourceFilter>();
services.AddTransient<TrnRequestHelper>();

builder.Services.AddOptions<EvidenceFilesOptions>()
.Bind(builder.Configuration.GetSection("EvidenceFiles"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,11 @@ public async Task<TrnRequestInfo> Handle(GetOrCreateTrnRequest request, Cancella
InductionRequired = request.InductionRequired,
UnderNewOverseasRegulations = request.UnderNewOverseasRegulations,
SlugId = request.SlugId,
TrnRequestId = TrnRequestHelper.GetCrmTrnRequestId(currentApplicationUserId, request.RequestId),
GetTrnToken = GetTrnToken
TrnRequestId = request.RequestId,
GetTrnToken = GetTrnToken,
ApplicationUserId = currentApplicationUserId,
IdentityVerified = request.IdentityVerified,
OneLoginUserSubject = request.OneLoginUserSubject
});

if (!createTeacherResult.Succeeded)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public class GetOrCreateTrnRequest : IRequest<TrnRequestInfo>
public bool? InductionRequired { get; set; }
public bool? UnderNewOverseasRegulations { get; set; }
public string SlugId { get; set; }
public bool IdentityVerified { get; set; }
public string OneLoginUserSubject { get; set; }
}

public class GetOrCreateTrnRequestAddress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public record CreateTrnRequestCommand
public required DateOnly DateOfBirth { get; init; }
public required IReadOnlyCollection<string> EmailAddresses { get; init; }
public required string? NationalInsuranceNumber { get; init; }
public required string? VerifiedOneLoginUserSubject { get; init; }
public required bool? IdentityVerified { get; init; }
public required string? OneLoginUserSubject { get; init; }
}

public class CreateTrnRequestHandler(
Expand All @@ -33,7 +34,8 @@ public class CreateTrnRequestHandler(
#pragma warning disable CS9113 // Parameter is unread.
INameSynonymProvider nameSynonymProvider,
#pragma warning restore CS9113 // Parameter is unread.
MessageSerializer messageSerializer)
MessageSerializer messageSerializer,
IClock clock)
{
public async Task<TrnRequestInfo> Handle(CreateTrnRequestCommand command)
{
Expand Down Expand Up @@ -118,13 +120,18 @@ await dbContext.TpsEmployments
var emailAddress = command.EmailAddresses?.FirstOrDefault();

var outboxMessages = new List<dfeta_TrsOutboxMessage>();
if (command.VerifiedOneLoginUserSubject is string oneLoginUserId)
if (command.OneLoginUserSubject is string oneLoginUserId)
{
outboxMessages.Add(messageSerializer.CreateCrmOutboxMessage(new TrnRequestMetadataMessage()
{
ApplicationUserId = currentApplicationUserId,
RequestId = command.RequestId,
VerifiedOneLoginUserSubject = oneLoginUserId
CreatedOn = clock.UtcNow,
IdentityVerified = command.IdentityVerified,
OneLoginUserSubject = oneLoginUserId,
Name = GetNonEmptyValues(command.FirstName, command.MiddleName, command.LastName),
DateOfBirth = command.DateOfBirth,
EmailAddress = emailAddress
}));
}

Expand Down Expand Up @@ -162,5 +169,20 @@ await crmQueryDispatcher.ExecuteQuery(new CreateContactQuery()
Trn = trn,
Status = status
};

static string[] GetNonEmptyValues(params string?[] values)
{
var result = new List<string>(values.Length);

foreach (var value in values)
{
if (!string.IsNullOrEmpty(value))
{
result.Add(value);
}
}

return result.ToArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ public class GetTrnRequestHandler(TrnRequestHelper trnRequestHelper, ICurrentUse

var contact = trnRequest.Contact;

// If we have metadata for the One Login user, ensure they're added to the OneLoginUsers table.
// FUTURE: when TRN requests are handled exclusively in TRS this should be done at the point the task is resolved instead of here.
if (trnRequest.Completed)
{
var metadata = await trnRequestHelper.GetRequestMetadata(trnRequest.ApplicationUserId, command.RequestId);

if (metadata?.VerifiedOneLoginUserSubject is string oneLoginUserId)
{
await trnRequestHelper.EnsureOneLoginUserIsConnected(trnRequest, oneLoginUserId);
}
}

return new TrnRequestInfo()
{
RequestId = command.RequestId.ToString(),
Expand All @@ -45,7 +33,7 @@ public class GetTrnRequestHandler(TrnRequestHelper trnRequestHelper, ICurrentUse
DateOfBirth = contact.BirthDate!.Value.ToDateOnlyWithDqtBstFix(isLocalTime: false)
},
Trn = contact.dfeta_TRN,
Status = trnRequest.Completed ? TrnRequestStatus.Completed : TrnRequestStatus.Pending,
Status = trnRequest.IsCompleted ? TrnRequestStatus.Completed : TrnRequestStatus.Pending,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public async Task<IActionResult> CreateTrnRequest(
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.Email is string emailAddress ? [emailAddress] : [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = null
IdentityVerified = null,
OneLoginUserSubject = null
};
var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public async Task<IActionResult> CreateTrnRequest(
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.EmailAddresses ?? [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = null
IdentityVerified = null,
OneLoginUserSubject = null
};
var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public async Task<IActionResult> CreateTrnRequest(
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.EmailAddresses ?? [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = request.VerifiedOneLoginUserSubject
IdentityVerified = request.IdentityVerified,
OneLoginUserSubject = request.OneLoginUserSubject
};
var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ namespace TeachingRecordSystem.Api.V3.VNext.Requests;
[GenerateVersionedDto(typeof(V20240606.Requests.CreateTrnRequestRequest))]
public partial record CreateTrnRequestRequest
{
public string? VerifiedOneLoginUserSubject { get; init; }
public bool IdentityVerified { get; init; }
public string? OneLoginUserSubject { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class SignInJourneyHelper(
AuthorizeAccessLinkGenerator linkGenerator,
IOptions<AuthorizeAccessOptions> optionsAccessor,
IUserInstanceStateProvider userInstanceStateProvider,
IClock clock)
IClock clock,
TrnRequestHelper trnRequestHelper)
{
public const string AuthenticationOnlyVtr = @"[""Cl.Cm""]";
public const string AuthenticationAndIdentityVerificationVtr = @"[""Cl.Cm.P2""]";
Expand Down Expand Up @@ -106,6 +107,16 @@ public async Task OnUserAuthenticated(JourneyInstance<SignInJourneyState> journe
}
}

string? trn = oneLoginUser.Person?.Trn;

if (oneLoginUser.PersonId is null &&
await TryMatchToTrnRequest(oneLoginUser) is { } matchResult)
{
trn = matchResult.Trn;
oneLoginUser.FirstSignIn ??= clock.UtcNow;
oneLoginUser.LastSignIn = clock.UtcNow;
}

await dbContext.SaveChangesAsync();

await journeyInstance.UpdateStateAsync(state =>
Expand All @@ -120,7 +131,7 @@ await journeyInstance.UpdateStateAsync(state =>
state.SetVerified(oneLoginUser.VerifiedNames!, oneLoginUser.VerifiedDatesOfBirth!);
}

if (oneLoginUser.Person?.Trn is string trn && !ShowDebugPages)
if (trn is not null && !ShowDebugPages)
{
Complete(state, trn);
}
Expand Down Expand Up @@ -329,6 +340,51 @@ public async Task<bool> TryMatchToTeachingRecord(JourneyInstance<SignInJourneySt
return false;
}

private async Task<TryMatchToTrnRequestResult?> TryMatchToTrnRequest(OneLoginUser oneLoginUser)
{
Debug.Assert(oneLoginUser.Email is not null);

var trnRequestMetadataForUser = await dbContext.TrnRequestMetadata
.Where(m => m.OneLoginUserSubject == oneLoginUser.Subject || m.EmailAddress == oneLoginUser.Email)
.ToArrayAsync();

if (trnRequestMetadataForUser is not [var trnRequestMetadata])
{
return null;
}

if (trnRequestMetadata.IdentityVerified != true)
{
return null;
}

var trnRequest = await trnRequestHelper.GetTrnRequestInfo(trnRequestMetadata.ApplicationUserId, trnRequestMetadata.RequestId);
if (trnRequest is null)
{
Debug.Fail("TRN request does not exist.");
return null;
}

if (!trnRequest.IsCompleted)
{
return null;
}

oneLoginUser.SetVerified(
verifiedOn: trnRequestMetadata.CreatedOn,
route: OneLoginUserVerificationRoute.External,
verifiedByApplicationUserId: trnRequestMetadata.ApplicationUserId,
verifiedNames: [trnRequestMetadata.Name],
verifiedDatesOfBirth: [trnRequestMetadata.DateOfBirth]);

oneLoginUser.SetMatched(
trnRequest.Contact.Id,
route: OneLoginUserMatchRoute.TrnRequest,
matchedAttributes: null);

return new(trnRequest.Contact.dfeta_TRN);
}

public void Complete(SignInJourneyState state, string trn)
{
if (state.OneLoginAuthenticationTicket is null)
Expand Down Expand Up @@ -382,4 +438,6 @@ private record TryMatchToIdentityUserResult(
string Trn,
OneLoginUserMatchRoute? MatchRoute,
IReadOnlyCollection<KeyValuePair<OneLoginUserMatchedAttribute, string>>? MatchedAttributes);

private record TryMatchToTrnRequestResult(string Trn);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public void Configure(EntityTypeBuilder<TrnRequestMetadata> builder)
{
builder.HasKey(r => new { r.ApplicationUserId, r.RequestId });
builder.Property(r => r.RequestId).IsRequired().HasMaxLength(TrnRequest.RequestIdMaxLength);
builder.Property(o => o.VerifiedOneLoginUserSubject).HasMaxLength(255);
builder.Property(r => r.OneLoginUserSubject).HasMaxLength(255);
builder.HasIndex(r => r.OneLoginUserSubject);
builder.HasIndex(r => r.EmailAddress);
}
}
Loading

0 comments on commit 168fd8b

Please sign in to comment.