mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 23:48:16 +08:00
[New feature] Support claims to path transformation (#968)
* Add the option to change DownstreamPath based on Claims * Add tests for Claims to downstream path
This commit is contained in:

committed by
Thiago Loureiro

parent
b32850a804
commit
8117366313
@ -19,6 +19,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private Dictionary<string, string> _routeClaimRequirement;
|
||||
private bool _isAuthorised;
|
||||
private List<ClaimToThing> _claimToQueries;
|
||||
private List<ClaimToThing> _claimToDownstreamPath;
|
||||
private string _requestIdHeaderKey;
|
||||
private bool _isCached;
|
||||
private CacheOptions _fileCacheOptions;
|
||||
@ -127,6 +128,12 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithClaimsToDownstreamPath(List<ClaimToThing> input)
|
||||
{
|
||||
_claimToDownstreamPath = input;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithIsCached(bool input)
|
||||
{
|
||||
_isCached = input;
|
||||
@ -186,7 +193,7 @@ namespace Ocelot.Configuration.Builder
|
||||
_serviceName = serviceName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public DownstreamReRouteBuilder WithServiceNamespace(string serviceNamespace)
|
||||
{
|
||||
_serviceNamespace = serviceNamespace;
|
||||
@ -265,6 +272,7 @@ namespace Ocelot.Configuration.Builder
|
||||
_claimToQueries,
|
||||
_claimsToHeaders,
|
||||
_claimToClaims,
|
||||
_claimToDownstreamPath,
|
||||
_isAuthenticated,
|
||||
_isAuthorised,
|
||||
_authenticationOptions,
|
||||
|
@ -86,6 +86,8 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
|
||||
|
||||
var claimsToDownstreamPath = _claimsToThingCreator.Create(fileReRoute.ChangeDownstreamPathTemplate);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod);
|
||||
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration);
|
||||
@ -114,6 +116,7 @@ namespace Ocelot.Configuration.Creator
|
||||
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||
.WithIsAuthorised(fileReRouteOptions.IsAuthorised)
|
||||
.WithClaimsToQueries(claimsToQueries)
|
||||
.WithClaimsToDownstreamPath(claimsToDownstreamPath)
|
||||
.WithRequestIdKey(requestIdKey)
|
||||
.WithIsCached(fileReRouteOptions.IsCached)
|
||||
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
|
||||
|
@ -28,6 +28,7 @@ namespace Ocelot.Configuration
|
||||
List<ClaimToThing> claimsToQueries,
|
||||
List<ClaimToThing> claimsToHeaders,
|
||||
List<ClaimToThing> claimsToClaims,
|
||||
List<ClaimToThing> claimsToPath,
|
||||
bool isAuthenticated,
|
||||
bool isAuthorised,
|
||||
AuthenticationOptions authenticationOptions,
|
||||
@ -63,6 +64,7 @@ namespace Ocelot.Configuration
|
||||
ClaimsToQueries = claimsToQueries ?? new List<ClaimToThing>();
|
||||
ClaimsToHeaders = claimsToHeaders ?? new List<ClaimToThing>();
|
||||
ClaimsToClaims = claimsToClaims ?? new List<ClaimToThing>();
|
||||
ClaimsToPath = claimsToPath ?? new List<ClaimToThing>();
|
||||
IsAuthenticated = isAuthenticated;
|
||||
IsAuthorised = isAuthorised;
|
||||
AuthenticationOptions = authenticationOptions;
|
||||
@ -93,6 +95,7 @@ namespace Ocelot.Configuration
|
||||
public List<ClaimToThing> ClaimsToQueries { get; }
|
||||
public List<ClaimToThing> ClaimsToHeaders { get; }
|
||||
public List<ClaimToThing> ClaimsToClaims { get; }
|
||||
public List<ClaimToThing> ClaimsToPath { get; }
|
||||
public bool IsAuthenticated { get; }
|
||||
public bool IsAuthorised { get; }
|
||||
public AuthenticationOptions AuthenticationOptions { get; }
|
||||
|
@ -11,6 +11,7 @@ namespace Ocelot.Configuration.File
|
||||
AddClaimsToRequest = new Dictionary<string, string>();
|
||||
RouteClaimsRequirement = new Dictionary<string, string>();
|
||||
AddQueriesToRequest = new Dictionary<string, string>();
|
||||
ChangeDownstreamPathTemplate = new Dictionary<string, string>();
|
||||
DownstreamHeaderTransform = new Dictionary<string, string>();
|
||||
FileCacheOptions = new FileCacheOptions();
|
||||
QoSOptions = new FileQoSOptions();
|
||||
@ -34,6 +35,7 @@ namespace Ocelot.Configuration.File
|
||||
public Dictionary<string, string> AddClaimsToRequest { get; set; }
|
||||
public Dictionary<string, string> RouteClaimsRequirement { get; set; }
|
||||
public Dictionary<string, string> AddQueriesToRequest { get; set; }
|
||||
public Dictionary<string, string> ChangeDownstreamPathTemplate { get; set; }
|
||||
public string RequestIdKey { get; set; }
|
||||
public FileCacheOptions FileCacheOptions { get; set; }
|
||||
public bool ReRouteIsCaseSensitive { get; set; }
|
||||
|
@ -25,6 +25,7 @@ namespace Ocelot.DependencyInjection
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.PathManipulation;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.RateLimit;
|
||||
using Ocelot.Request.Creator;
|
||||
@ -92,6 +93,7 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
||||
Services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||
Services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||
Services.TryAddSingleton<IChangeDownstreamPathTemplate, ChangeDownstreamPathTemplate>();
|
||||
Services.TryAddSingleton<IClaimsParser, ClaimsParser>();
|
||||
Services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
Services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
|
@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.PathManipulation
|
||||
{
|
||||
public class ChangeDownstreamPathTemplate : IChangeDownstreamPathTemplate
|
||||
{
|
||||
private readonly IClaimsParser _claimsParser;
|
||||
|
||||
public ChangeDownstreamPathTemplate(IClaimsParser claimsParser)
|
||||
{
|
||||
_claimsParser = claimsParser;
|
||||
}
|
||||
|
||||
public Response ChangeDownstreamPath(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims,
|
||||
DownstreamPathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> placeholders)
|
||||
{
|
||||
foreach (var config in claimsToThings)
|
||||
{
|
||||
var value = _claimsParser.GetValue(claims, config.NewKey, config.Delimiter, config.Index);
|
||||
|
||||
if (value.IsError)
|
||||
{
|
||||
return new ErrorResponse(value.Errors);
|
||||
}
|
||||
|
||||
var placeholderName = $"{{{config.ExistingKey}}}";
|
||||
|
||||
if (!downstreamPathTemplate.Value.Contains(placeholderName))
|
||||
{
|
||||
return new ErrorResponse(new CouldNotFindPlaceholderError(placeholderName));
|
||||
}
|
||||
|
||||
if (placeholders.Any(ph => ph.Name == placeholderName))
|
||||
{
|
||||
placeholders.RemoveAll(ph => ph.Name == placeholderName);
|
||||
}
|
||||
|
||||
placeholders.Add(new PlaceholderNameAndValue(placeholderName, value.Data));
|
||||
}
|
||||
|
||||
return new OkResponse();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
|
||||
namespace Ocelot.PathManipulation
|
||||
{
|
||||
public interface IChangeDownstreamPathTemplate
|
||||
{
|
||||
Response ChangeDownstreamPath(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims,
|
||||
DownstreamPathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> placeholders);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.PathManipulation.Middleware
|
||||
{
|
||||
public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate;
|
||||
|
||||
public ClaimsToDownstreamPathMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IChangeDownstreamPathTemplate changeDownstreamPathTemplate)
|
||||
: base(loggerFactory.CreateLogger<ClaimsToDownstreamPathMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_changeDownstreamPathTemplate = changeDownstreamPathTemplate;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToPath.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path");
|
||||
var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(context.DownstreamReRoute.ClaimsToPath, context.HttpContext.User.Claims,
|
||||
context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
Logger.LogWarning("there was an error setting queries on context, setting pipeline error");
|
||||
|
||||
SetPipelineError(context, response.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.PathManipulation.Middleware
|
||||
{
|
||||
public static class ClaimsToDownstreamPathMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToDownstreamPathMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToDownstreamPathMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using Ocelot.DownstreamUrlCreator.Middleware;
|
||||
using Ocelot.Errors.Middleware;
|
||||
using Ocelot.Headers.Middleware;
|
||||
using Ocelot.LoadBalancer.Middleware;
|
||||
using Ocelot.PathManipulation.Middleware;
|
||||
using Ocelot.QueryStrings.Middleware;
|
||||
using Ocelot.RateLimit.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
@ -118,6 +119,8 @@ namespace Ocelot.Middleware.Pipeline
|
||||
// Now we can run any claims to query string transformation middleware
|
||||
builder.UseClaimsToQueryStringMiddleware();
|
||||
|
||||
builder.UseClaimsToDownstreamPathMiddleware();
|
||||
|
||||
// Get the load balancer for this request
|
||||
builder.UseLoadBalancingMiddleware();
|
||||
|
||||
|
Reference in New Issue
Block a user