Skip to content

Commit 7274b33

Browse files
AhmadiRaminCopilot
andcommitted
[DSI-8678] Fix getting results for user access
Co-authored-by: Copilot <copilot@github.com>
1 parent ed07c5f commit 7274b33

11 files changed

Lines changed: 102 additions & 40 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ bld/
4141
[Ll]og/
4242
[Ll]ogs/
4343
_site/
44+
graphifyer-output/
4445

4546
# Visual Studio 2015/2017 cache/options directory
4647
.vs/
@@ -403,3 +404,6 @@ FodyWeavers.xsd
403404

404405
# GitHub workflows
405406
/build.sln
407+
src/Dfe.SignIn.AppHost/appsettings.Development.json
408+
.gitignore
409+
src/Dfe.SignIn.AppHost/appsettings.tran.json

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
<PackageVersion Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.3.0" />
5353
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.2" />
5454
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.2.0" />
55-
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
55+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
5656
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
5757
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" />
5858
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />

src/Dfe.SignIn.AppHost/AppHost.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
var publicApiSecretConfig = builder.Configuration.GetSection("PublicApiSecretEncryption");
2626
var selectOrgConfig = builder.Configuration.GetSection("SelectOrganisation");
2727
var internalApiConfig = builder.Configuration.GetSection("InternalApiClient");
28+
var efConfig = builder.Configuration.GetSection("EntityFramework");
2829
var redisConnectionString = redis.Resource.ConnectionStringExpression;
2930

3031
builder.AddProject<Projects.Dfe_SignIn_Web_Help>("app-help", launchProfileName: "http")
@@ -55,15 +56,38 @@
5556
.WaitFor(frontend)
5657
.WaitFor(redis);
5758

59+
var internalApi = builder.AddProject<Projects.Dfe_SignIn_InternalApi>("app-internal-api", launchProfileName: "http")
60+
.WithEnvironment("ASPNETCORE_ENVIRONMENT", builder.Configuration["ASPNETCORE_ENVIRONMENT"] ?? "Local")
61+
.WithEnvironment("EntityFramework__Directories__Host", efConfig["Directories:Host"])
62+
.WithEnvironment("EntityFramework__Directories__Name", efConfig["Directories:Name"])
63+
.WithEnvironment("EntityFramework__Directories__Username", efConfig["Directories:Username"])
64+
.WithEnvironment("EntityFramework__Directories__Password", efConfig["Directories:Password"])
65+
.WithEnvironment("EntityFramework__Organisations__Host", efConfig["Organisations:Host"])
66+
.WithEnvironment("EntityFramework__Organisations__Name", efConfig["Organisations:Name"])
67+
.WithEnvironment("EntityFramework__Organisations__Username", efConfig["Organisations:Username"])
68+
.WithEnvironment("EntityFramework__Organisations__Password", efConfig["Organisations:Password"])
69+
.WithEnvironment("InternalApiClient__ClientId", internalApiConfig["ClientId"])
70+
.WithEnvironment("InternalApiClient__ClientSecret", internalApiConfig["ClientSecret"])
71+
.WithEnvironment("InternalApiClient__Tenant", internalApiConfig["Tenant"])
72+
.WithEnvironment("InternalApiClient__HostUrl", internalApiConfig["HostUrl"])
73+
.WithEnvironment("InternalApiClient__Search__BaseAddress", internalApiConfig["Search:BaseAddress"])
74+
.WithEnvironment("InternalApiClient__Access__BaseAddress", internalApiConfig["Access:BaseAddress"])
75+
.WithEnvironment("InternalApiClient__Organisations__BaseAddress", internalApiConfig["Organisations:BaseAddress"])
76+
.WithEnvironment("InternalApiClient__Directories__BaseAddress", internalApiConfig["Directories:BaseAddress"])
77+
.WithEnvironment("InternalApiClient__Applications__BaseAddress", internalApiConfig["Applications:BaseAddress"])
78+
.WithEnvironment("InternalApiClient__UseProxy", "false");
79+
5880
builder.AddProject<Projects.Dfe_SignIn_PublicApi>("app-public-api", launchProfileName: "http")
5981
.WithSharedConfiguration(builder.Configuration, frontend.GetEndpoint("http"))
6082
.WithEnvironment("SelectOrganisationSessionRedisCache__ConnectionString", redisConnectionString)
6183
.WithEnvironment("InteractionsRedisCache__ConnectionString", redisConnectionString)
6284
.WithEnvironment("BearerToken__ValidAudience", bearerTokenConfig["ValidAudience"])
6385
.WithEnvironment("PublicApiSecretEncryption__Key", publicApiSecretConfig["Key"])
6486
.WithEnvironment("SelectOrganisation__SelectOrganisationBaseAddress", selectOrgConfig["SelectOrganisationBaseAddress"])
87+
.WithEnvironment("InternalApiClient__BaseAddress", internalApi.GetEndpoint("http"))
6588
.WithEnvironment("InternalApiClient__Access__BaseAddress", internalApiConfig["Access:BaseAddress"])
6689
.WithEnvironment("InternalApiClient__Organisations__BaseAddress", internalApiConfig["Organisations:BaseAddress"])
90+
.WaitFor(internalApi)
6791
.WaitFor(redis);
6892

6993
builder.AddExecutable("tool-tls-proxy", "pwsh", "../../", "-Command", "Start-DsiTlsProxy");

src/Dfe.SignIn.AppHost/Dfe.SignIn.AppHost.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<ItemGroup>
44
<ProjectReference Include="..\Dfe.SignIn.Web.Help\Dfe.SignIn.Web.Help.csproj" />
55
<ProjectReference Include="..\Dfe.SignIn.Web.Profile\Dfe.SignIn.Web.Profile.csproj" />
6+
<ProjectReference Include="..\Dfe.SignIn.InternalApi\Dfe.SignIn.InternalApi.csproj" />
67
<ProjectReference Include="..\Dfe.SignIn.PublicApi\Dfe.SignIn.PublicApi.csproj" />
78
</ItemGroup>
89

src/Dfe.SignIn.Core.Contracts/SelectOrganisation/FilterOrganisationsForUser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.ComponentModel.DataAnnotations;
22
using Dfe.SignIn.Base.Framework;
3+
using Dfe.SignIn.Core.Contracts.Applications;
34
using Dfe.SignIn.Core.Contracts.Organisations;
45
using Dfe.SignIn.Core.Public.SelectOrganisation;
56

@@ -9,6 +10,7 @@ namespace Dfe.SignIn.Core.Contracts.SelectOrganisation;
910
/// Represents a request to filter organisations for a user.
1011
/// </summary>
1112
[AssociatedResponse(typeof(FilterOrganisationsForUserResponse))]
13+
[Throws(typeof(ApplicationNotFoundException))]
1214
public sealed record FilterOrganisationsForUserRequest
1315
{
1416
/// <summary>

src/Dfe.SignIn.InternalApi.Client/InternalApiRequester.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,21 @@ public async Task<object> InvokeAsync(
3535
var httpResponse = await client.SendAsJsonAsync(
3636
HttpMethod.Post, endpointPath, context.Request, cancellationToken);
3737

38+
var jsonOptions = jsonOptionsAccessor.Get(JsonHelperExtensions.StandardOptionsKey);
39+
3840
if (httpResponse.StatusCode is HttpStatusCode.OK) {
3941
var response = (await httpResponse.Content.ReadFromJsonAsync<InteractionResponse>(cancellationToken))!;
40-
var jsonOptions = jsonOptionsAccessor.Get(JsonHelperExtensions.StandardOptionsKey);
4142
var responseType = ContractsAssembly.GetType(response.Type, throwOnError: true)!;
4243
return JsonSerializer.Deserialize(response.Data, responseType, jsonOptions)!;
4344
}
44-
else if (httpResponse.StatusCode is HttpStatusCode.BadRequest or HttpStatusCode.InternalServerError) {
45-
var failedResponse = (await httpResponse.Content.ReadFromJsonAsync<FailedInteractionResponse>(cancellationToken))!;
45+
else if (httpResponse.StatusCode is HttpStatusCode.BadRequest or HttpStatusCode.NotFound or HttpStatusCode.InternalServerError) {
46+
var body = await httpResponse.Content.ReadAsStringAsync(cancellationToken);
47+
if (string.IsNullOrWhiteSpace(body)) {
48+
throw new HttpRequestException(
49+
$"Internal API returned {(int)httpResponse.StatusCode} with an empty body for '{endpointPath}'. " +
50+
$"The endpoint may not be registered or a server error occurred before the response was written.");
51+
}
52+
var failedResponse = JsonSerializer.Deserialize<FailedInteractionResponse>(body, jsonOptions)!;
4653
var exception = exceptionSerializer.DeserializeExceptionFromJson(failedResponse.Exception);
4754
throw exception;
4855
}

src/Dfe.SignIn.InternalApi/Configuration/UserUseCaseExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static IServiceCollection AddUserUseCases(
3131
services.AddInteractor<ChangeJobTitleUseCase>();
3232
services.AddInteractor<CreateUserUseCase>();
3333
services.AddInteractor<GetOrganisationsAssociatedWithUserUseCase>();
34+
services.AddInteractor<GetUserOrganisationIdentifiersUseCase>();
3435
services.AddInteractor<GetUserProfileUseCase>();
3536
services.AddInteractor<GetUserStatusUseCase>();
3637
services.AddInteractor<LinkEntraUserToDsiUseCase>();

src/Dfe.SignIn.InternalApi/Endpoints/InteractionEndpoints.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public static void UseUserEndpoints(this WebApplication app)
6161
app.Map<CheckIsBlockedEmailAddressRequest, CheckIsBlockedEmailAddressResponse>();
6262
app.Map<CreateUserRequest, CreateUserResponse>();
6363
app.Map<GetOrganisationsAssociatedWithUserRequest, GetOrganisationsAssociatedWithUserResponse>();
64+
app.Map<GetUserOrganisationIdentifiersRequest, GetUserOrganisationIdentifiersResponse>();
6465
app.Map<GetUserProfileRequest, GetUserProfileResponse>();
6566
app.Map<GetUserStatusRequest, GetUserStatusResponse>();
6667
app.Map<LinkEntraUserToDsiRequest, LinkEntraUserToDsiResponse>();

src/Dfe.SignIn.NodeApi.Client/Access/GetUserServiceAccessNodeRequester.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ public override async Task<GetUserServiceAccessResponse> InvokeAsync(
3535
UserId = dto.UserId,
3636
ServiceId = dto.ServiceId,
3737
OrganisationId = dto.OrganisationId,
38-
Roles = dto.Roles.Select(r => new UserServiceRole {
38+
Roles = (dto.Roles ?? []).Select(r => new UserServiceRole {
3939
Id = r.Id,
4040
Name = r.Name,
4141
Code = r.Code,
4242
NumericId = r.NumericId,
4343
}),
44-
Identifiers = dto.Identifiers.Select(i => new UserServiceIdentifier {
44+
Identifiers = (dto.Identifiers ?? []).Select(i => new UserServiceIdentifier {
4545
Key = i.Key,
4646
Value = i.Value,
4747
}),
Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using Dfe.SignIn.Base.Framework;
2+
using Dfe.SignIn.Core.Contracts.Applications;
23
using Dfe.SignIn.Core.Contracts.SelectOrganisation;
34
using Dfe.SignIn.PublicApi.Authorization;
45
using Dfe.SignIn.PublicApi.Contracts.SelectOrganisation;
6+
using Microsoft.AspNetCore.Http.HttpResults;
57
using Microsoft.AspNetCore.Mvc;
68

79
namespace Dfe.SignIn.PublicApi.Endpoints.SelectOrganisation;
@@ -11,27 +13,36 @@ public static partial class SelectOrganisationEndpoints
1113
/// <summary>
1214
/// Create a URL which the end-user can use to select an organisation.
1315
/// </summary>
14-
public static async Task<CreateSelectOrganisationSessionApiResponse> PostSelectOrganisationSession(
16+
/// <returns>
17+
/// <para>201 with the session URL and whether options are available.</para>
18+
/// <para>403 when the client application cannot be found.</para>
19+
/// </returns>
20+
public static async Task<Results<Ok<CreateSelectOrganisationSessionApiResponse>, ForbidHttpResult>> PostSelectOrganisationSession(
1521
[FromBody] CreateSelectOrganisationSessionApiRequestBody request,
1622
// ---
1723
IClientSession session,
1824
IInteractionDispatcher interaction)
1925
{
20-
var response = await interaction.DispatchAsync(
21-
new CreateSelectOrganisationSessionRequest {
22-
ClientId = session.ClientId,
23-
UserId = request.UserId,
24-
Prompt = request.Prompt,
25-
Filter = request.Filter,
26-
AllowCancel = request.AllowCancel,
27-
CallbackUrl = request.CallbackUrl,
28-
}
29-
).To<CreateSelectOrganisationSessionResponse>();
26+
try {
27+
var response = await interaction.DispatchAsync(
28+
new CreateSelectOrganisationSessionRequest {
29+
ClientId = session.ClientId,
30+
UserId = request.UserId,
31+
Prompt = request.Prompt,
32+
Filter = request.Filter,
33+
AllowCancel = request.AllowCancel,
34+
CallbackUrl = request.CallbackUrl,
35+
}
36+
).To<CreateSelectOrganisationSessionResponse>();
3037

31-
return new CreateSelectOrganisationSessionApiResponse {
32-
RequestId = response.RequestId,
33-
HasOptions = response.HasOptions,
34-
Url = response.Url,
35-
};
38+
return TypedResults.Ok(new CreateSelectOrganisationSessionApiResponse {
39+
RequestId = response.RequestId,
40+
HasOptions = response.HasOptions,
41+
Url = response.Url,
42+
});
43+
}
44+
catch (ApplicationNotFoundException) {
45+
return TypedResults.Forbid();
46+
}
3647
}
3748
}

0 commit comments

Comments
 (0)