Skip to content

Commit

Permalink
Fix issue with routing around the identities hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
axelmarquezh committed Apr 22, 2024
1 parent fd65fb8 commit dd0b2e4
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ namespace EdFi.Ods.Api.Attributes;
/// <summary>
/// Applied to a Controller, this introduces the necessary root route template for accessing an ODS instance (including route values for the tenant and ODS, as configured for the API).
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class ApplyOdsRouteRootTemplateAttribute : Attribute { }
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ public virtual async Task<IActionResult> Put([FromBody] TPutRequest request, Gui
return ValidationFailedResult(result.ValidationResults);
}

var resourceUri = new Uri(GetResourceUrl());
var resourceUri = new Uri(Request.ResourceUri(_reverseProxySettings));
Response.GetTypedHeaders().Location = resourceUri;
Response.GetTypedHeaders().ETag = GetEtag(result.ETag);

Expand Down Expand Up @@ -413,7 +413,7 @@ public virtual async Task<IActionResult> Post([FromBody] TPostRequest request)
return ValidationFailedResult(result.ValidationResults);
}

var resourceUri = new Uri($"{GetResourceUrl()}/{result.ResourceId.GetValueOrDefault():n}");
var resourceUri = new Uri($"{Request.ResourceUri(_reverseProxySettings)}/{result.ResourceId.GetValueOrDefault():n}");
Response.GetTypedHeaders().Location = resourceUri;
Response.GetTypedHeaders().ETag = GetEtag(result.ETag);

Expand Down Expand Up @@ -474,24 +474,6 @@ private EntityTagHeaderValue GetEtag(string etagValue)
return new EntityTagHeaderValue(Quoted(etagValue));
}

protected string GetResourceUrl()
{
try
{
var uriBuilder = new UriBuilder(
Request.Scheme(this._reverseProxySettings),
Request.Host(this._reverseProxySettings),
Request.Port(this._reverseProxySettings),
Request.PathBase.Add(Request.Path));

return uriBuilder.Uri.ToString().TrimEnd('/');
}
catch (Exception ex)
{
throw new Exception("Unable to parse API base URL from request.", ex);
}
}

private static string Quoted(string text) => "\"" + text + "\"";

private static string Unquoted(string text) => text?.Trim('"');
Expand Down
13 changes: 12 additions & 1 deletion Application/EdFi.Ods.Api/Extensions/HttpRequestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ public static string RootUrl(this HttpRequest request, ReverseProxySettings reve
return uriBuilder.Uri.AbsoluteUri.TrimEnd('/');
}

public static string ResourceUri(this HttpRequest request, ReverseProxySettings reverseProxySettings)
public static string ResourceUri(this HttpRequest request, ReverseProxySettings reverseProxySettings, bool useParentPath = false)
{
var uriBuilder = new UriBuilder(
request.Scheme(reverseProxySettings),
request.Host(reverseProxySettings),
request.Port(reverseProxySettings),
request.PathBase.Add(request.Path));

if (useParentPath)
{
uriBuilder = GetParentPath(uriBuilder.Uri);
}

return uriBuilder.Uri.ToString().TrimEnd('/');
}

Expand Down Expand Up @@ -141,5 +146,11 @@ public static bool TryGetRequestHeader(this HttpRequest request, string headerNa

return !string.IsNullOrEmpty(value);
}

private static UriBuilder GetParentPath(Uri uri)
{
var parentPath = string.Concat(uri.Segments.SkipLast(1));
return new UriBuilder(uri.Scheme, uri.Host, uri.Port, parentPath);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using System.Net;
using System.Threading.Tasks;
using EdFi.Ods.Api.Attributes;
using EdFi.Ods.Api.Extensions;
using EdFi.Ods.Common.Configuration;
using EdFi.Ods.Common.Exceptions;
using EdFi.Ods.Features.IdentityManagement;
using EdFi.Ods.Features.IdentityManagement.Models;
Expand Down Expand Up @@ -41,15 +43,18 @@ public abstract class IdentitiesControllerBase<TCreateRequest, TSearchRequest, T
{
private const string InvalidServerResponse = "Invalid response from identity service: ";
private const string NoIdentitySystem = "There is no integrated Unique Identity System";
private const string ResultsRoute = "results";

private readonly IIdentityService<TCreateRequest, TSearchRequest, TSearchResponse, TIdentityResponse> _identitySubsystem;
private readonly IIdentityServiceAsync<TSearchRequest, TSearchResponse, TIdentityResponse> _identitySubsystemAsync;
private readonly ReverseProxySettings _reverseProxySettings;

protected IdentitiesControllerBase(IIdentityService<TCreateRequest, TSearchRequest, TSearchResponse, TIdentityResponse> identitySubsystem,
IIdentityServiceAsync<TSearchRequest, TSearchResponse, TIdentityResponse> identitySubsystemAsync)
IIdentityServiceAsync<TSearchRequest, TSearchResponse, TIdentityResponse> identitySubsystemAsync, ApiSettings apiSettings)
{
_identitySubsystem = identitySubsystem;
_identitySubsystemAsync = identitySubsystemAsync;
_reverseProxySettings = apiSettings.GetReverseProxySettings();
}

/// <summary>
Expand All @@ -63,7 +68,7 @@ protected IdentitiesControllerBase(IIdentityService<TCreateRequest, TSearchReque
/// <response code="502">The underlying identity system returned an error.</response>
/// <returns>The identity information for the provided Unique Id</returns>
[HttpGet]
[Route("{id}", Name = "IdentitiesGetById")]
[Route("{id}")]
public async Task<IActionResult> GetById([FromRoute(Name = "id")] string uniqueId)
{
try
Expand Down Expand Up @@ -123,8 +128,8 @@ public async Task<IActionResult> Create([FromBody] TCreateRequest request)
switch (result.StatusCode)
{
case IdentityStatusCode.Success:
var route = Url.Link("IdentitiesGetById", new { id = result.Data });
return Created(new Uri(route), result.Data);
var route = new Uri($"{Request.ResourceUri(_reverseProxySettings)}/{result.Data}");
return Created(route, result.Data);
case IdentityStatusCode.NotFound:
return NotFound(new NotFoundException());
case IdentityStatusCode.InvalidProperties:
Expand Down Expand Up @@ -161,7 +166,7 @@ public async Task<IActionResult> Find([FromBody] string[] uniqueIds)
switch (result.StatusCode)
{
case IdentityStatusCode.Success:
var route = Url.Link("IdentitiesSearchResult", new { id = result.Data });
var route = new Uri($"{Request.ResourceUri(_reverseProxySettings, true)}/{ResultsRoute}/{result.Data}");
return Accepted(route);
case IdentityStatusCode.NotFound:
return NotFound(InvalidServerResponse + "Not Found");
Expand Down Expand Up @@ -211,7 +216,7 @@ public async Task<IActionResult> Search([FromBody] TSearchRequest[] criteria)
switch (result.StatusCode)
{
case IdentityStatusCode.Success:
var route = Url.Link("IdentitiesSearchResult", new { id = result.Data });
var route = new Uri($"{Request.ResourceUri(_reverseProxySettings, true)}/{ResultsRoute}/{result.Data}");
return Accepted(route);
case IdentityStatusCode.NotFound:
return NotFound(InvalidServerResponse + "Not Found");
Expand Down Expand Up @@ -244,12 +249,12 @@ public async Task<IActionResult> Search([FromBody] TSearchRequest[] criteria)
/// <response code="501">The server does not support the requested function.</response>
/// <response code="502">The underlying identity system returned an error.</response>
[HttpGet]
[Route("results/{id}", Name = "IdentitiesSearchResult")]
[Route($"{ResultsRoute}/{{id}}")]
public async Task<IActionResult> Result([FromRoute(Name = "id")] string searchToken)
{
try
{
if ((_identitySubsystem.IdentityServiceCapabilities & IdentityServiceCapabilities.Results) == 0)
if ((_identitySubsystemAsync.IdentityServiceCapabilities & IdentityServiceCapabilities.Results) == 0)
{
return NotImplemented();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using EdFi.Ods.Common.Configuration;
using EdFi.Ods.Features.Controllers;
using EdFi.Ods.Features.IdentityManagement.Models;

Expand All @@ -23,6 +24,7 @@ public class IdentitiesController
{
public IdentitiesController(
IIdentityServiceWithDefaultModels identitySubsystem,
IIdentityServiceWithDefaultModelsAsync identitySubsystemAsync)
: base(identitySubsystem, identitySubsystemAsync) { }
IIdentityServiceWithDefaultModelsAsync identitySubsystemAsync,
ApiSettings apiSettings)
: base(identitySubsystem, identitySubsystemAsync, apiSettings) { }
}
Loading

0 comments on commit dd0b2e4

Please sign in to comment.