mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 12:30:49 +08:00 
			
		
		
		
	Added ability to strip claims and forward to downstream service as headers
This commit is contained in:
		@@ -1,4 +1,6 @@
 | 
			
		||||
namespace Ocelot.Library.Builder
 | 
			
		||||
using Ocelot.Library.RequestBuilder;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.Builder
 | 
			
		||||
{
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using Configuration;
 | 
			
		||||
@@ -16,6 +18,7 @@
 | 
			
		||||
        private List<string> _additionalScopes;
 | 
			
		||||
        private bool _requireHttps;
 | 
			
		||||
        private string _scopeSecret;
 | 
			
		||||
        private List<ConfigurationHeaderExtractorProperties> _configHeaderExtractorProperties;
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder()
 | 
			
		||||
        {
 | 
			
		||||
@@ -86,9 +89,15 @@
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRouteBuilder WithConfigurationHeaderExtractorProperties(List<ConfigurationHeaderExtractorProperties> input)
 | 
			
		||||
        {
 | 
			
		||||
            _configHeaderExtractorProperties = input;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ReRoute Build()
 | 
			
		||||
        {
 | 
			
		||||
            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret));
 | 
			
		||||
            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,8 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Ocelot.Library.RequestBuilder;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.Configuration
 | 
			
		||||
{
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
@@ -11,11 +16,18 @@ namespace Ocelot.Library.Configuration
 | 
			
		||||
        private readonly List<ReRoute> _reRoutes;
 | 
			
		||||
        private const string RegExMatchEverything = ".*";
 | 
			
		||||
        private const string RegExMatchEndString = "$";
 | 
			
		||||
        private readonly IConfigurationHeaderExtrator _configurationHeaderExtrator;
 | 
			
		||||
        private readonly ILogger<OcelotConfiguration> _logger;
 | 
			
		||||
 | 
			
		||||
        public OcelotConfiguration(IOptions<YamlConfiguration> options, IConfigurationValidator configurationValidator)
 | 
			
		||||
        public OcelotConfiguration(IOptions<YamlConfiguration> options, 
 | 
			
		||||
            IConfigurationValidator configurationValidator, 
 | 
			
		||||
            IConfigurationHeaderExtrator configurationHeaderExtrator, 
 | 
			
		||||
            ILogger<OcelotConfiguration> logger)
 | 
			
		||||
        {
 | 
			
		||||
            _options = options;
 | 
			
		||||
            _configurationValidator = configurationValidator;
 | 
			
		||||
            _configurationHeaderExtrator = configurationHeaderExtrator;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _reRoutes = new List<ReRoute>();
 | 
			
		||||
            SetUpConfiguration();
 | 
			
		||||
        }
 | 
			
		||||
@@ -43,7 +55,7 @@ namespace Ocelot.Library.Configuration
 | 
			
		||||
 | 
			
		||||
            var placeholders = new List<string>();
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < upstreamTemplate.Length; i++)
 | 
			
		||||
            for (var i = 0; i < upstreamTemplate.Length; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (IsPlaceHolder(upstreamTemplate, i))
 | 
			
		||||
                {
 | 
			
		||||
@@ -70,17 +82,41 @@ namespace Ocelot.Library.Configuration
 | 
			
		||||
                    reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
 | 
			
		||||
                    reRoute.AuthenticationOptions.ScopeSecret);
 | 
			
		||||
 | 
			
		||||
                var configHeaders = GetHeadersToAddToRequest(reRoute);
 | 
			
		||||
 | 
			
		||||
                _reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
 | 
			
		||||
                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, authOptionsForRoute
 | 
			
		||||
                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, 
 | 
			
		||||
                    authOptionsForRoute, configHeaders
 | 
			
		||||
                    ));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod,
 | 
			
		||||
                    upstreamTemplate, isAuthenticated, null));
 | 
			
		||||
                    upstreamTemplate, isAuthenticated, null, new List<ConfigurationHeaderExtractorProperties>()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private List<ConfigurationHeaderExtractorProperties> GetHeadersToAddToRequest(YamlReRoute reRoute)
 | 
			
		||||
        {
 | 
			
		||||
            var configHeaders = new List<ConfigurationHeaderExtractorProperties>();
 | 
			
		||||
 | 
			
		||||
            foreach (var add in reRoute.AddHeadersToRequest)
 | 
			
		||||
            {
 | 
			
		||||
                var configurationHeader = _configurationHeaderExtrator.Extract(add.Key, add.Value);
 | 
			
		||||
 | 
			
		||||
                if (configurationHeader.IsError)
 | 
			
		||||
                {
 | 
			
		||||
                    _logger.LogCritical(new EventId(1, "Application Failed to start"),
 | 
			
		||||
                        $"Unable to extract configuration for key: {add.Key} and value: {add.Value} your configuration file is incorrect");
 | 
			
		||||
 | 
			
		||||
                    throw new Exception(configurationHeader.Errors[0].Message);
 | 
			
		||||
                }
 | 
			
		||||
                configHeaders.Add(configurationHeader.Data);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return configHeaders;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool IsPlaceHolder(string upstreamTemplate, int i)
 | 
			
		||||
        {
 | 
			
		||||
            return upstreamTemplate[i] == '{';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
namespace Ocelot.Library.Configuration
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Ocelot.Library.RequestBuilder;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class ReRoute
 | 
			
		||||
    {
 | 
			
		||||
        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions)
 | 
			
		||||
        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ConfigurationHeaderExtractorProperties> configurationHeaderExtractorProperties)
 | 
			
		||||
        {
 | 
			
		||||
            DownstreamTemplate = downstreamTemplate;
 | 
			
		||||
            UpstreamTemplate = upstreamTemplate;
 | 
			
		||||
@@ -10,6 +13,8 @@
 | 
			
		||||
            UpstreamTemplatePattern = upstreamTemplatePattern;
 | 
			
		||||
            IsAuthenticated = isAuthenticated;
 | 
			
		||||
            AuthenticationOptions = authenticationOptions;
 | 
			
		||||
            ConfigurationHeaderExtractorProperties = configurationHeaderExtractorProperties 
 | 
			
		||||
                ?? new List<ConfigurationHeaderExtractorProperties>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string DownstreamTemplate { get; private set; }
 | 
			
		||||
@@ -18,5 +23,6 @@
 | 
			
		||||
        public string UpstreamHttpMethod { get; private set; }
 | 
			
		||||
        public bool IsAuthenticated { get; private set; }
 | 
			
		||||
        public AuthenticationOptions AuthenticationOptions { get; private set; }
 | 
			
		||||
        public List<ConfigurationHeaderExtractorProperties> ConfigurationHeaderExtractorProperties { get; private set; } 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,6 @@
 | 
			
		||||
        public string UpstreamTemplate { get; set; }
 | 
			
		||||
        public string UpstreamHttpMethod { get; set; }
 | 
			
		||||
        public YamlAuthenticationOptions AuthenticationOptions { get; set; }
 | 
			
		||||
        public Dictionary<string,string> AddHeadersToRequest { get; set; }
 | 
			
		||||
        public Dictionary<string, string> AddHeadersToRequest { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -21,6 +21,9 @@
 | 
			
		||||
            services.Configure<YamlConfiguration>(configurationRoot);
 | 
			
		||||
 | 
			
		||||
            // Add framework services.
 | 
			
		||||
            services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
 | 
			
		||||
            services.AddSingleton<IClaimsParser, ClaimsParser>();
 | 
			
		||||
            services.AddSingleton<IConfigurationHeaderExtrator, ConfigurationHeaderExtrator>();
 | 
			
		||||
            services.AddSingleton<IConfigurationValidator, ConfigurationValidator>();
 | 
			
		||||
            services.AddSingleton<IOcelotConfiguration, OcelotConfiguration>();
 | 
			
		||||
            services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,10 @@
 | 
			
		||||
        CannotFindDataError,
 | 
			
		||||
        UnableToCompleteRequestError,
 | 
			
		||||
        UnableToCreateAuthenticationHandlerError,
 | 
			
		||||
        UnsupportedAuthenticationProviderError
 | 
			
		||||
        UnsupportedAuthenticationProviderError,
 | 
			
		||||
        CannotFindClaimError,
 | 
			
		||||
        ParsingConfigurationHeaderError,
 | 
			
		||||
        NoInstructionsError,
 | 
			
		||||
        InstructionNotForClaimsError
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
namespace Ocelot.Library.Middleware
 | 
			
		||||
{
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Microsoft.AspNetCore.Http;
 | 
			
		||||
    using Repository;
 | 
			
		||||
 | 
			
		||||
    public class ClaimsParserMiddleware : OcelotMiddleware
 | 
			
		||||
    {
 | 
			
		||||
        private readonly RequestDelegate _next;
 | 
			
		||||
 | 
			
		||||
        public ClaimsParserMiddleware(RequestDelegate next, IScopedRequestDataRepository scopedRequestDataRepository) 
 | 
			
		||||
            : base(scopedRequestDataRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _next = next;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Invoke(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
            await _next.Invoke(context);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Microsoft.Extensions.Primitives;
 | 
			
		||||
using Ocelot.Library.DownstreamRouteFinder;
 | 
			
		||||
using Ocelot.Library.RequestBuilder;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.Middleware
 | 
			
		||||
{
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Microsoft.AspNetCore.Http;
 | 
			
		||||
    using Repository;
 | 
			
		||||
 | 
			
		||||
    public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
 | 
			
		||||
    {
 | 
			
		||||
        private readonly RequestDelegate _next;
 | 
			
		||||
        private readonly IAddHeadersToRequest _addHeadersToRequest;
 | 
			
		||||
        private readonly IScopedRequestDataRepository _scopedRequestDataRepository;
 | 
			
		||||
 | 
			
		||||
        public HttpRequestHeadersBuilderMiddleware(RequestDelegate next, 
 | 
			
		||||
            IScopedRequestDataRepository scopedRequestDataRepository,
 | 
			
		||||
            IAddHeadersToRequest addHeadersToRequest) 
 | 
			
		||||
            : base(scopedRequestDataRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _next = next;
 | 
			
		||||
            _addHeadersToRequest = addHeadersToRequest;
 | 
			
		||||
            _scopedRequestDataRepository = scopedRequestDataRepository;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Invoke(HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            var downstreamRoute = _scopedRequestDataRepository.Get<DownstreamRoute>("DownstreamRoute");
 | 
			
		||||
 | 
			
		||||
            if (downstreamRoute.Data.ReRoute.ConfigurationHeaderExtractorProperties.Any())
 | 
			
		||||
            {
 | 
			
		||||
                _addHeadersToRequest.SetHeadersOnContext(downstreamRoute.Data.ReRoute.ConfigurationHeaderExtractorProperties, context);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            await _next.Invoke(context);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
namespace Ocelot.Library.Middleware
 | 
			
		||||
{
 | 
			
		||||
    using Microsoft.AspNetCore.Builder;
 | 
			
		||||
 | 
			
		||||
    public static class HttpRequestHeadersBuilderMiddlewareExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static IApplicationBuilder UseHttpRequestHeadersBuilderMiddleware(this IApplicationBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            return builder.UseMiddleware<HttpRequestHeadersBuilderMiddleware>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,6 +12,8 @@
 | 
			
		||||
 | 
			
		||||
            builder.UseAuthenticationMiddleware();
 | 
			
		||||
 | 
			
		||||
            builder.UseHttpRequestHeadersBuilderMiddleware();
 | 
			
		||||
 | 
			
		||||
            builder.UseDownstreamUrlCreatorMiddleware();
 | 
			
		||||
 | 
			
		||||
            builder.UseHttpRequestBuilderMiddleware();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								src/Ocelot.Library/RequestBuilder/AddHeadersToRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Ocelot.Library/RequestBuilder/AddHeadersToRequest.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.Primitives;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class AddHeadersToRequest : IAddHeadersToRequest
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IClaimsParser _claimsParser;
 | 
			
		||||
 | 
			
		||||
        public AddHeadersToRequest(IClaimsParser claimsParser)
 | 
			
		||||
        {
 | 
			
		||||
            _claimsParser = claimsParser;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Response SetHeadersOnContext(List<ConfigurationHeaderExtractorProperties> configurationHeaderExtractorProperties, HttpContext context)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var config in configurationHeaderExtractorProperties)
 | 
			
		||||
            {
 | 
			
		||||
                var value = _claimsParser.GetValue(context.User.Claims, config.ClaimKey, config.Delimiter, config.Index);
 | 
			
		||||
 | 
			
		||||
                if (value.IsError)
 | 
			
		||||
                {
 | 
			
		||||
                    return new ErrorResponse(value.Errors);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var exists = context.Request.Headers.FirstOrDefault(x => x.Key == config.HeaderKey);
 | 
			
		||||
 | 
			
		||||
                if (!string.IsNullOrEmpty(exists.Key))
 | 
			
		||||
                {
 | 
			
		||||
                    context.Request.Headers.Remove(exists);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                context.Request.Headers.Add(config.HeaderKey, new StringValues(value.Data));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new OkResponse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/CannotFindClaimError.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/CannotFindClaimError.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class CannotFindClaimError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public CannotFindClaimError(string message) 
 | 
			
		||||
            : base(message, OcelotErrorCode.CannotFindClaimError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								src/Ocelot.Library/RequestBuilder/ClaimsParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/Ocelot.Library/RequestBuilder/ClaimsParser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class ClaimsParser : IClaimsParser
 | 
			
		||||
    {
 | 
			
		||||
        public Response<string> GetValue(IEnumerable<Claim> claims, string key, string delimiter, int index)
 | 
			
		||||
        {
 | 
			
		||||
            var claimResponse = GetValue(claims, key);
 | 
			
		||||
 | 
			
		||||
            if (claimResponse.IsError)
 | 
			
		||||
            {
 | 
			
		||||
                return claimResponse;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrEmpty(delimiter))
 | 
			
		||||
            {
 | 
			
		||||
                return claimResponse;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var splits = claimResponse.Data.Split(delimiter.ToCharArray());
 | 
			
		||||
 | 
			
		||||
            if (splits.Length < index || index < 0)
 | 
			
		||||
            {
 | 
			
		||||
                return new ErrorResponse<string>(new List<Error>
 | 
			
		||||
                {
 | 
			
		||||
                    new CannotFindClaimError($"Cannot find claim for key: {key}, delimiter: {delimiter}, index: {index}")
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var value = splits[index];
 | 
			
		||||
 | 
			
		||||
            return new OkResponse<string>(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Response<string> GetValue(IEnumerable<Claim> claims, string key)
 | 
			
		||||
        {
 | 
			
		||||
            var claim = claims.FirstOrDefault(c => c.Type == key);
 | 
			
		||||
 | 
			
		||||
            if (claim != null)
 | 
			
		||||
            {
 | 
			
		||||
                return new OkResponse<string>(claim.Value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new ErrorResponse<string>(new List<Error>
 | 
			
		||||
            {
 | 
			
		||||
                new CannotFindClaimError($"Cannot find claim for key: {key}")
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class ConfigurationHeaderExtractorProperties
 | 
			
		||||
    {
 | 
			
		||||
        public ConfigurationHeaderExtractorProperties(string headerKey, string claimKey, string delimiter, int index)
 | 
			
		||||
        {
 | 
			
		||||
            ClaimKey = claimKey;
 | 
			
		||||
            Delimiter = delimiter;
 | 
			
		||||
            Index = index;
 | 
			
		||||
            HeaderKey = headerKey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string HeaderKey { get; private set; }
 | 
			
		||||
        public string ClaimKey { get; private set; }
 | 
			
		||||
        public string Delimiter { get; private set; }
 | 
			
		||||
        public int Index { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								src/Ocelot.Library/RequestBuilder/HeaderExtrator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/Ocelot.Library/RequestBuilder/HeaderExtrator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class ConfigurationHeaderExtrator : IConfigurationHeaderExtrator
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Regex _claimRegex = new Regex("Claims\\[.*\\]");
 | 
			
		||||
        private readonly Regex _indexRegex = new Regex("value\\[.*\\]");
 | 
			
		||||
        private const string SplitToken = ">";
 | 
			
		||||
 | 
			
		||||
        public Response<ConfigurationHeaderExtractorProperties> Extract(string headerKey, string value)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var instructions = value.Split(SplitToken.ToCharArray());
 | 
			
		||||
 | 
			
		||||
                if (instructions.Length <= 1)
 | 
			
		||||
                {
 | 
			
		||||
                    return new ErrorResponse<ConfigurationHeaderExtractorProperties>(
 | 
			
		||||
                        new List<Error>
 | 
			
		||||
                    {
 | 
			
		||||
                        new NoInstructionsError(SplitToken)
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var claimMatch = _claimRegex.IsMatch(instructions[0]);
 | 
			
		||||
 | 
			
		||||
                if (!claimMatch)
 | 
			
		||||
                {
 | 
			
		||||
                    return new ErrorResponse<ConfigurationHeaderExtractorProperties>(
 | 
			
		||||
                        new List<Error>
 | 
			
		||||
                        {
 | 
			
		||||
                            new InstructionNotForClaimsError()
 | 
			
		||||
                        });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var claimKey = GetIndexValue(instructions[0]);
 | 
			
		||||
                var index = 0;
 | 
			
		||||
                var delimiter = string.Empty;
 | 
			
		||||
 | 
			
		||||
                if (instructions.Length > 2 && _indexRegex.IsMatch(instructions[1]))
 | 
			
		||||
                {
 | 
			
		||||
                    index = int.Parse(GetIndexValue(instructions[1]));
 | 
			
		||||
                    delimiter = instructions[2].Trim();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new OkResponse<ConfigurationHeaderExtractorProperties>(
 | 
			
		||||
                               new ConfigurationHeaderExtractorProperties(headerKey, claimKey, delimiter, index));
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception exception)
 | 
			
		||||
            {
 | 
			
		||||
                return new ErrorResponse<ConfigurationHeaderExtractorProperties>(
 | 
			
		||||
                    new List<Error>
 | 
			
		||||
                    {
 | 
			
		||||
                        new ParsingConfigurationHeaderError(exception)
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string GetIndexValue(string instruction)
 | 
			
		||||
        {
 | 
			
		||||
            var firstIndexer = instruction.IndexOf("[", StringComparison.Ordinal);
 | 
			
		||||
            var lastIndexer = instruction.IndexOf("]", StringComparison.Ordinal);
 | 
			
		||||
            var length = lastIndexer - firstIndexer;
 | 
			
		||||
            var claimKey = instruction.Substring(firstIndexer + 1, length - 1);
 | 
			
		||||
            return claimKey;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/IAddHeadersToRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/IAddHeadersToRequest.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public interface IAddHeadersToRequest
 | 
			
		||||
    {
 | 
			
		||||
        Response SetHeadersOnContext(List<ConfigurationHeaderExtractorProperties> configurationHeaderExtractorProperties,
 | 
			
		||||
            HttpContext context);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/Ocelot.Library/RequestBuilder/IClaimsParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Ocelot.Library/RequestBuilder/IClaimsParser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public interface IClaimsParser
 | 
			
		||||
    {
 | 
			
		||||
        Response<string> GetValue(IEnumerable<Claim> claims, string key, string delimiter, int index);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Ocelot.Library.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public interface IConfigurationHeaderExtrator
 | 
			
		||||
    {
 | 
			
		||||
        Response<ConfigurationHeaderExtractorProperties> Extract(string headerKey, string value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class InstructionNotForClaimsError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public InstructionNotForClaimsError() 
 | 
			
		||||
            : base("instructions did not contain claims, at the moment we only support claims extraction", OcelotErrorCode.InstructionNotForClaimsError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/NoInstructionsError.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ocelot.Library/RequestBuilder/NoInstructionsError.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class NoInstructionsError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public NoInstructionsError(string splitToken) 
 | 
			
		||||
            : base($"There we no instructions splitting on {splitToken}", OcelotErrorCode.NoInstructionsError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,13 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Ocelot.Library.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Library.RequestBuilder
 | 
			
		||||
{
 | 
			
		||||
    public class ParsingConfigurationHeaderError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public ParsingConfigurationHeaderError(Exception exception) 
 | 
			
		||||
            : base($"error parsing configuration eception is {exception.Message}", OcelotErrorCode.ParsingConfigurationHeaderError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,18 +12,14 @@
 | 
			
		||||
    {
 | 
			
		||||
        public async Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response)
 | 
			
		||||
        {
 | 
			
		||||
            if (response.IsSuccessStatusCode)
 | 
			
		||||
            context.Response.OnStarting(x =>
 | 
			
		||||
            {
 | 
			
		||||
                context.Response.OnStarting(x =>
 | 
			
		||||
                {
 | 
			
		||||
                    context.Response.StatusCode = (int)response.StatusCode;
 | 
			
		||||
                    return Task.CompletedTask;
 | 
			
		||||
                }, context);
 | 
			
		||||
                context.Response.StatusCode = (int)response.StatusCode;
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            }, context);
 | 
			
		||||
 | 
			
		||||
                await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
 | 
			
		||||
                return context;
 | 
			
		||||
            }
 | 
			
		||||
            return context;
 | 
			
		||||
            await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
 | 
			
		||||
            return context;       
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<HttpContext> CreateErrorResponse(HttpContext context, int statusCode)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user