diff --git a/README.md b/README.md index 4c69c3cc..222d9abc 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio * Request Aggregation * Service Discovery with Consul & Eureka * Service Fabric +* Kubernetes * WebSockets * Authentication * Authorisation diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst index cb2ec08d..ba230a81 100644 --- a/docs/features/authentication.rst +++ b/docs/features/authentication.rst @@ -138,26 +138,39 @@ Then map the authentication provider key to a ReRoute in your configuration e.g. Okta ^^^^ -Add nuget package : `"Okta.AspNetCore" https://www.nuget.org/packages/Okta.AspNetCore/`_ +Add the following to your startup Configure method: -In a StartUp.cs file add to a method Configure next lines: -app.UseAuthentication(); -app.UseOcelot().Wait(); +.. code-block:: csharp -In a StartUp.cs file add to a method ConfigureServices lines: + app + .UseAuthentication() + .UseOcelot() + .Wait(); + + +Add the following, at minimum, to your startup ConfigureServices method: + +.. code-block:: csharp + + services + .AddAuthentication() + .AddJwtBearer(oktaProviderKey, options => + { + options.Audience = configuration["Authentication:Okta:Audience"]; // Okta Authorization server Audience + options.Authority = configuration["Authentication:Okta:Server"]; // Okta Authorization Issuer URI URL e.g. https://{subdomain}.okta.com/oauth2/{authidentifier} + }); + services.AddOcelot(configuration); + + +NOTE: In order to get Ocelot to view the scope claim from Okta properly, you have to add the following to map the default Okta "scp" claim to "scope" + + +.. code-block:: csharp + + // Map Okta scp to scope claims instead of http://schemas.microsoft.com/identity/claims/scope to allow ocelot to read/verify them + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("scp"); + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("scp", "scope"); -services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme; - options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme; - options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme; - }) - .AddOktaWebApi(new OktaWebApiOptions - { - OktaDomain = _cfg["Okta:OktaDomain"] - - }); -services.AddOcelot(_cfg); `Issue 446 `_ that contains some code and examples that might help with Okta integration. diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index 289bb211..9f8c8f9f 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -14,11 +14,22 @@ Then add the following to your ConfigureServices method. s.AddOcelot() .AddKubernetes(); -If you have services deployed in kubernetes you will normally use the naming service to access them. +If you have services deployed in kubernetes you will normally use the naming service to access them. Default usePodServiceAccount = True, which means that ServiceAccount using Pod to access the service of the k8s cluster needs to be ServiceAccount based on RABC authorization + +.. code-block::csharp + public static class OcelotBuilderExtensions + { + public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true); + } + +You can replicate a Permissive. Using RBAC role bindings. +`Permissive RBAC Permissions `_, k8s api server and token will read from pod . + +.. code-block::json +kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the -kubernetes service name. We also need to set up the ServiceDiscoveryProvider in -GlobalConfiguration. The example here shows a typical configuration. It assumes kubernetes api server is running on 192.168.0.13 and that api service is on port 443. +kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration. .. code-block:: json @@ -43,19 +54,21 @@ GlobalConfiguration. The example here shows a typical configuration. It assumes } } } +Service deployment in Namespace Dev , ServiceDiscoveryProvider type is kube, you also can set pollkube ServiceDiscoveryProvider type. + Note: Host、 Port and Token are no longer in use。 You use Ocelot to poll kubernetes for latest service information rather than per request. If you want to poll kubernetes for the latest services rather than per request (default behaviour) then you need to set the following configuration. .. code-block:: json -"ServiceDiscoveryProvider": { + "ServiceDiscoveryProvider": { "Host": "192.168.0.13", - "Port": 443, - "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG", - "Namespace": "dev", - "Type": "pollkube" - "PollingInterval": 100 -} + "Port": 443, + "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG", + "Namespace": "dev", + "Type": "pollkube" + "PollingInterval": 100 + } The polling interval is in milliseconds and tells Ocelot how often to call kubernetes for changes in service configuration. diff --git a/docs/features/routing.rst b/docs/features/routing.rst index 9a52fb19..0f2e5efc 100644 --- a/docs/features/routing.rst +++ b/docs/features/routing.rst @@ -181,7 +181,7 @@ matched /goods/{catchAll} (because this is the first ReRoute in the list!). Dynamic Routing ^^^^^^^^^^^^^^^ -This feature was requested in `issue 340 `_. +This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if this sounds interesting to you. diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst index 55d446ab..f36f9f78 100644 --- a/docs/features/servicediscovery.rst +++ b/docs/features/servicediscovery.rst @@ -113,7 +113,7 @@ Ocelot will add this token to the Consul client that it uses to make requests an Eureka ^^^^^^ -This feature was requested as part of `Issue 262 `_ . to add support for Netflix's +This feature was requested as part of `Issue 262 `_ . to add support for Netflix's Eureka service discovery provider. The main reason for this is it is a key part of `Steeltoe `_ which is something to do with `Pivotal `_! Anyway enough of the background. @@ -158,7 +158,7 @@ is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them Dynamic Routing ^^^^^^^^^^^^^^^ -This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segment of the upstream path to lookup the downstream service with the service discovery provider. +This feature was requested in `issue 340 `_. The idea is to enable dynamic routing when using a service discovery provider (see that section of the docs for more info). In this mode Ocelot will use the first segment of the upstream path to lookup the downstream service with the service discovery provider. An example of this would be calling Ocelot with a url like https://api.mywebsite.com/product/products. Ocelot will take the first segment of the path which is product and use it as a key to look up the service in Consul. If Consul returns a service Ocelot will request it on whatever host and port comes back from Consul plus the remaining path segments in this case products thus making the downstream call http://hostfromconsul:portfromconsul/products. Ocelot will apprend any query string to the downstream url as normal. diff --git a/samples/AdministrationApi/README.md b/samples/AdministrationApi/README.md index 4477bf9b..36ed2a67 100644 --- a/samples/AdministrationApi/README.md +++ b/samples/AdministrationApi/README.md @@ -1,3 +1,4 @@ +```json { "reRoutes": [ { @@ -89,4 +90,5 @@ "useProxy": true } } -} \ No newline at end of file +} +``` diff --git a/samples/OelotKube/ApiGateway/ApiGateway.csproj b/samples/OelotKube/ApiGateway/ApiGateway.csproj index aef8c150..0dc4f632 100644 --- a/samples/OelotKube/ApiGateway/ApiGateway.csproj +++ b/samples/OelotKube/ApiGateway/ApiGateway.csproj @@ -10,11 +10,8 @@ - - - - - + + diff --git a/samples/OelotKube/ApiGateway/Dockerfile b/samples/OelotKube/ApiGateway/Dockerfile index 19bd33c2..1e3fcd11 100644 --- a/samples/OelotKube/ApiGateway/Dockerfile +++ b/samples/OelotKube/ApiGateway/Dockerfile @@ -1,12 +1,10 @@ -FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.1-stretch-slim AS base WORKDIR /app EXPOSE 80 -FROM microsoft/dotnet:2.1-sdk AS build +FROM mcr.microsoft.com/dotnet/core/sdk:2.1-stretch AS build WORKDIR /src COPY ["ApiGateway/ApiGateway.csproj", "ApiGateway/"] -COPY ["../../src/Ocelot/Ocelot.csproj", "../../src/Ocelot/"] -COPY ["../../src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj", "../../src/Ocelot.Provider.Kubernetes/"] RUN dotnet restore "ApiGateway/ApiGateway.csproj" COPY . . WORKDIR "/src/ApiGateway" @@ -18,4 +16,4 @@ RUN dotnet publish "ApiGateway.csproj" -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . -ENTRYPOINT ["dotnet", "ApiGateway.dll"] +ENTRYPOINT ["dotnet", "ApiGateway.dll"] \ No newline at end of file diff --git a/samples/OelotKube/OelotKube.sln b/samples/OelotKube/OelotKube.sln index aa57e0dd..295fe248 100644 --- a/samples/OelotKube/OelotKube.sln +++ b/samples/OelotKube/OelotKube.sln @@ -1,14 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2048 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28803.202 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGateway", "ApiGateway\ApiGateway.csproj", "{E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot", "..\..\src\Ocelot\Ocelot.csproj", "{E8551073-622E-45FA-AD09-038EB8AAFFBC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Kubernetes", "..\..\src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{EF973868-98A6-4864-BF66-65B5A8C123FE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownstreamService", "DownstreamService\DownstreamService.csproj", "{86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}" EndProject Global @@ -21,14 +17,6 @@ Global {E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9AFBFD7-EF20-48E5-BB30-5C63C59D7C1C}.Release|Any CPU.Build.0 = Release|Any CPU - {E8551073-622E-45FA-AD09-038EB8AAFFBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8551073-622E-45FA-AD09-038EB8AAFFBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8551073-622E-45FA-AD09-038EB8AAFFBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8551073-622E-45FA-AD09-038EB8AAFFBC}.Release|Any CPU.Build.0 = Release|Any CPU - {EF973868-98A6-4864-BF66-65B5A8C123FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF973868-98A6-4864-BF66-65B5A8C123FE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF973868-98A6-4864-BF66-65B5A8C123FE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF973868-98A6-4864-BF66-65B5A8C123FE}.Release|Any CPU.Build.0 = Release|Any CPU {86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Debug|Any CPU.Build.0 = Debug|Any CPU {86FFAE3C-648F-4CDE-A260-37C8EBFBF4F2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs b/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs index d96ce919..e9841890 100644 --- a/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Cache.CacheManager/OcelotBuilderExtensions.cs @@ -33,6 +33,10 @@ builder.Services.RemoveAll(typeof(IOcelotCache)); builder.Services.AddSingleton>(fileConfigCacheManagerOutputCache); builder.Services.AddSingleton>(fileConfigCacheManager); + + builder.Services.RemoveAll(typeof(ICacheKeyGenerator)); + builder.Services.AddSingleton(); + return builder; } } diff --git a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs b/src/Ocelot.Provider.Kubernetes/KubeProvider.cs index 388dfb6a..5baa7e31 100644 --- a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs +++ b/src/Ocelot.Provider.Kubernetes/KubeProvider.cs @@ -15,13 +15,14 @@ namespace Ocelot.Provider.Kubernetes private IOcelotLogger logger; private IKubeApiClient kubeApi; - public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClientFactory kubeClientFactory) + public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClient kubeApi) { this.kubeRegistryConfiguration = kubeRegistryConfiguration; this.logger = factory.CreateLogger(); - this.kubeApi = kubeClientFactory.Get(kubeRegistryConfiguration); + this.kubeApi = kubeApi; } + public async Task> Get() { var service = await kubeApi.ServicesV1() diff --git a/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs b/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs index 8e5f800e..5c35bbeb 100644 --- a/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs +++ b/src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs @@ -5,16 +5,8 @@ namespace Ocelot.Provider.Kubernetes { public class KubeRegistryConfiguration { - public Uri ApiEndPoint { get; set; } - public string KubeNamespace { get; set; } public string KeyOfServiceInK8s { get; set; } - - public KubeAuthStrategy AuthStrategy { get; set; } - - public string AccessToken { get; set; } - - public bool AllowInsecure { get; set; } } } diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs index b0bb35d7..c33839c6 100644 --- a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs +++ b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs @@ -16,18 +16,14 @@ namespace Ocelot.Provider.Kubernetes private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, string name, IOcelotLoggerFactory factory) { - var kubeClientFactory = provider.GetService(); + var kubeClient = provider.GetService(); var k8sRegistryConfiguration = new KubeRegistryConfiguration() { - ApiEndPoint = new Uri($"https://{config.Host}:{config.Port}"), KeyOfServiceInK8s = name, KubeNamespace = config.Namespace, - AuthStrategy = KubeAuthStrategy.BearerToken, - AccessToken = config.Token, - AllowInsecure = true // Don't validate server certificate }; - var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClientFactory); + var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClient); if (config.Type?.ToLower() == "pollkube") { return new PollKube(config.PollingInterval, factory, k8sServiceDiscoveryProvider); diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj index dbe39207..13a73143 100644 --- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj +++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj @@ -25,7 +25,13 @@ - + + + + + + + diff --git a/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs index 80772167..f979586a 100644 --- a/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs @@ -1,14 +1,15 @@ -using Microsoft.Extensions.DependencyInjection; +using KubeClient; +using Microsoft.Extensions.DependencyInjection; using Ocelot.DependencyInjection; namespace Ocelot.Provider.Kubernetes { public static class OcelotBuilderExtensions { - public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder) + public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true) { builder.Services.AddSingleton(KubernetesProviderFactory.Get); - builder.Services.AddSingleton(); + builder.Services.AddKubeClient(usePodServiceAccount); return builder; } } diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs index 8fd910c8..75a166f2 100644 --- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs +++ b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs @@ -1,6 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Security.Claims; +using System.Text.RegularExpressions; + +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Middleware; using Ocelot.Responses; +using Ocelot.Values; namespace Ocelot.Authorisation { @@ -15,8 +22,11 @@ namespace Ocelot.Authorisation _claimsParser = claimsParser; } - public Response Authorise(ClaimsPrincipal claimsPrincipal, Dictionary routeClaimsRequirement) - { + public Response Authorise( + ClaimsPrincipal claimsPrincipal, + Dictionary routeClaimsRequirement, + List urlPathPlaceholderNameAndValues + ){ foreach (var required in routeClaimsRequirement) { var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key); @@ -27,12 +37,50 @@ namespace Ocelot.Authorisation } if (values.Data != null) - { - var authorised = values.Data.Contains(required.Value); - if (!authorised) + { + // dynamic claim + var match = Regex.Match(required.Value, @"^{(?.+)}$"); + if (match.Success) { - return new ErrorResponse(new ClaimValueNotAuthorisedError( - $"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}")); + var variableName = match.Captures[0].Value; + + var matchingPlaceholders = urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Take(2).ToArray(); + if (matchingPlaceholders.Length == 1) + { + // match + var actualValue = matchingPlaceholders[0].Value; + var authorised = values.Data.Contains(actualValue); + if (!authorised) + { + return new ErrorResponse(new ClaimValueNotAuthorisedError( + $"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}")); + } + } + else + { + // config error + if (matchingPlaceholders.Length == 0) + { + return new ErrorResponse(new ClaimValueNotAuthorisedError( + $"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p=>p.Name))}")); + } + else + { + return new ErrorResponse(new ClaimValueNotAuthorisedError( + $"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p=>p.Name.Equals(variableName)).Select(p => p.Value))}")); + } + } + + } + else + { + // static claim + var authorised = values.Data.Contains(required.Value); + if (!authorised) + { + return new ErrorResponse(new ClaimValueNotAuthorisedError( + $"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}")); + } } } else diff --git a/src/Ocelot/Authorisation/IClaimsAuthoriser.cs b/src/Ocelot/Authorisation/IClaimsAuthoriser.cs index 4abd799e..9f9ce3f8 100644 --- a/src/Ocelot/Authorisation/IClaimsAuthoriser.cs +++ b/src/Ocelot/Authorisation/IClaimsAuthoriser.cs @@ -1,5 +1,9 @@ using System.Security.Claims; + +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; +using Ocelot.Values; namespace Ocelot.Authorisation { @@ -7,6 +11,10 @@ namespace Ocelot.Authorisation public interface IClaimsAuthoriser { - Response Authorise(ClaimsPrincipal claimsPrincipal, Dictionary routeClaimsRequirement); + Response Authorise( + ClaimsPrincipal claimsPrincipal, + Dictionary routeClaimsRequirement, + List urlPathPlaceholderNameAndValues + ); } } diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index f3e4e21d..8e906d5c 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -57,8 +57,8 @@ if (IsAuthorisedRoute(context.DownstreamReRoute)) { Logger.LogInformation("route is authorised"); - - var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement); + + var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement, context.TemplatePlaceholderNameAndValues); if (authorised.IsError) { diff --git a/src/Ocelot/Cache/CacheKeyGenerator.cs b/src/Ocelot/Cache/CacheKeyGenerator.cs new file mode 100644 index 00000000..c33c6104 --- /dev/null +++ b/src/Ocelot/Cache/CacheKeyGenerator.cs @@ -0,0 +1,19 @@ +using System.Text; +using System.Threading.Tasks; +using Ocelot.Middleware; + +namespace Ocelot.Cache { + public class CacheKeyGenerator : ICacheKeyGenerator { + public string GenerateRequestCacheKey(DownstreamContext context) { + string hashedContent = null; + StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}"); + if (context.DownstreamRequest.Content != null) { + string requestContentString = Task.Run(async () => await context.DownstreamRequest.Content.ReadAsStringAsync()).Result; + downStreamUrlKeyBuilder.Append(requestContentString); + } + + hashedContent = MD5Helper.GenerateMd5(downStreamUrlKeyBuilder.ToString()); + return hashedContent; + } + } +} diff --git a/src/Ocelot/Cache/ICacheKeyGenerator.cs b/src/Ocelot/Cache/ICacheKeyGenerator.cs new file mode 100644 index 00000000..5a65eb8a --- /dev/null +++ b/src/Ocelot/Cache/ICacheKeyGenerator.cs @@ -0,0 +1,7 @@ +using Ocelot.Middleware; + +namespace Ocelot.Cache { + public interface ICacheKeyGenerator { + string GenerateRequestCacheKey(DownstreamContext context); + } +} diff --git a/src/Ocelot/Cache/MD5Helper.cs b/src/Ocelot/Cache/MD5Helper.cs new file mode 100644 index 00000000..d98bb8c5 --- /dev/null +++ b/src/Ocelot/Cache/MD5Helper.cs @@ -0,0 +1,22 @@ +using System.Security.Cryptography; +using System.Text; + +namespace Ocelot.Cache { + public static class MD5Helper { + public static string GenerateMd5(byte[] contentBytes) { + MD5 md5 = MD5.Create(); + byte[] hash = md5.ComputeHash(contentBytes); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hash.Length; i++) { + sb.Append(hash[i].ToString("X2")); + } + + return sb.ToString(); + } + + public static string GenerateMd5(string contentString) { + byte[] contentBytes = Encoding.Unicode.GetBytes(contentString); + return GenerateMd5(contentBytes); + } + } +} diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index 5b96e79c..925e90c6 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -7,19 +7,23 @@ using Ocelot.Logging; using Ocelot.Middleware; using System.IO; + using System.Text; public class OutputCacheMiddleware : OcelotMiddleware { private readonly OcelotRequestDelegate _next; private readonly IOcelotCache _outputCache; + private readonly ICacheKeyGenerator _cacheGeneratot; public OutputCacheMiddleware(OcelotRequestDelegate next, IOcelotLoggerFactory loggerFactory, - IOcelotCache outputCache) + IOcelotCache outputCache, + ICacheKeyGenerator cacheGeneratot) :base(loggerFactory.CreateLogger()) { _next = next; _outputCache = outputCache; + _cacheGeneratot = cacheGeneratot; } public async Task Invoke(DownstreamContext context) @@ -31,10 +35,11 @@ } var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}"; + string downStreamRequestCacheKey = _cacheGeneratot.GenerateRequestCacheKey(context); Logger.LogDebug($"Started checking cache for {downstreamUrlKey}"); - var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region); + var cached = _outputCache.Get(downStreamRequestCacheKey, context.DownstreamReRoute.CacheOptions.Region); if (cached != null) { @@ -61,12 +66,13 @@ cached = await CreateCachedResponse(context.DownstreamResponse); - _outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region); + _outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region); Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}"); } - private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response) + private void SetHttpResponseMessageThisRequest(DownstreamContext context, + DownstreamResponse response) { context.DownstreamResponse = response; } diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs index 3b1588d2..728c218f 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs @@ -122,7 +122,7 @@ { var matchingReRoutes = reRoutes .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate - && (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null)) + && (r.UpstreamHost == reRoute.UpstreamHost || reRoute.UpstreamHost == null)) .ToList(); if (matchingReRoutes.Count == 1) diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 9f81e11d..3fee15e7 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -107,6 +107,7 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.TryAddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository diff --git a/src/Ocelot/Middleware/Multiplexer/IDefinedAggregator.cs b/src/Ocelot/Middleware/Multiplexer/IDefinedAggregator.cs index b1585165..48234782 100644 --- a/src/Ocelot/Middleware/Multiplexer/IDefinedAggregator.cs +++ b/src/Ocelot/Middleware/Multiplexer/IDefinedAggregator.cs @@ -5,6 +5,6 @@ namespace Ocelot.Middleware.Multiplexer { public interface IDefinedAggregator { - Task Aggregate(List responses); + Task Aggregate(List responses); } } diff --git a/src/Ocelot/Middleware/Multiplexer/UserDefinedResponseAggregator.cs b/src/Ocelot/Middleware/Multiplexer/UserDefinedResponseAggregator.cs index aa44466d..0080b60e 100644 --- a/src/Ocelot/Middleware/Multiplexer/UserDefinedResponseAggregator.cs +++ b/src/Ocelot/Middleware/Multiplexer/UserDefinedResponseAggregator.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Ocelot.Configuration; @@ -21,7 +20,7 @@ namespace Ocelot.Middleware.Multiplexer if (!aggregator.IsError) { var aggregateResponse = await aggregator.Data - .Aggregate(downstreamResponses.Select(x => x.DownstreamResponse).ToList()); + .Aggregate(downstreamResponses); originalContext.DownstreamResponse = aggregateResponse; } diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index 78d32346..a841df5a 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -1,22 +1,22 @@ namespace Ocelot.Middleware { - using System; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Options; - using System.Diagnostics; using DependencyInjection; using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using Ocelot.Configuration; using Ocelot.Configuration.Creator; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Ocelot.Configuration.Setter; - using Ocelot.Responses; using Ocelot.Logging; using Ocelot.Middleware.Pipeline; - using Microsoft.Extensions.DependencyInjection; + using Ocelot.Responses; + using System; + using System.Diagnostics; + using System.Linq; + using System.Threading.Tasks; public static class OcelotMiddlewareExtensions { @@ -42,6 +42,30 @@ return CreateOcelotPipeline(builder, pipelineConfiguration); } + public static Task UseOcelot(this IApplicationBuilder app, Action builderAction) + => UseOcelot(app, builderAction, new OcelotPipelineConfiguration()); + + public static async Task UseOcelot(this IApplicationBuilder app, Action builderAction, OcelotPipelineConfiguration configuration) + { + await CreateConfiguration(app); // initConfiguration + + ConfigureDiagnosticListener(app); + + var ocelotPipelineBuilder = new OcelotPipelineBuilder(app.ApplicationServices); + builderAction?.Invoke(ocelotPipelineBuilder, configuration ?? new OcelotPipelineConfiguration()); + + var ocelotDelegate = ocelotPipelineBuilder.Build(); + app.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware"; + + app.Use(async (context, task) => + { + var downstreamContext = new DownstreamContext(context); + await ocelotDelegate.Invoke(downstreamContext); + }); + + return app; + } + private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices); diff --git a/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs index 9cb0db56..a10a2369 100644 --- a/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs +++ b/src/Ocelot/Middleware/Pipeline/IOcelotPipelineBuilder.cs @@ -9,7 +9,7 @@ namespace Ocelot.Middleware.Pipeline public interface IOcelotPipelineBuilder { IServiceProvider ApplicationServices { get; } - OcelotPipelineBuilder Use(Func middleware); + IOcelotPipelineBuilder Use(Func middleware); OcelotRequestDelegate Build(); IOcelotPipelineBuilder New(); } diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs index 5877ab62..58f71122 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilder.cs @@ -27,7 +27,7 @@ namespace Ocelot.Middleware.Pipeline public IServiceProvider ApplicationServices { get; } - public OcelotPipelineBuilder Use(Func middleware) + public IOcelotPipelineBuilder Use(Func middleware) { _middlewares.Add(middleware); return this; diff --git a/src/Ocelot/Request/Middleware/DownstreamRequest.cs b/src/Ocelot/Request/Middleware/DownstreamRequest.cs index 665039d0..49d27efe 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequest.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequest.cs @@ -19,6 +19,7 @@ namespace Ocelot.Request.Middleware Headers = _request.Headers; AbsolutePath = _request.RequestUri.AbsolutePath; Query = _request.RequestUri.Query; + Content = _request.Content; } public HttpRequestHeaders Headers { get; } @@ -37,6 +38,8 @@ namespace Ocelot.Request.Middleware public string Query { get; set; } + public HttpContent Content { get; set; } + public HttpRequestMessage ToHttpRequestMessage() { var uriBuilder = new UriBuilder diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index e3d99854..6de4d8c7 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -646,14 +646,14 @@ namespace Ocelot.AcceptanceTests _dep = dep; } - public async Task Aggregate(List responses) + public async Task Aggregate(List responses) { - var one = await responses[0].Content.ReadAsStringAsync(); - var two = await responses[1].Content.ReadAsStringAsync(); + var one = await responses[0].DownstreamResponse.Content.ReadAsStringAsync(); + var two = await responses[1].DownstreamResponse.Content.ReadAsStringAsync(); var merge = $"{one}, {two}"; merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", ""); - var headers = responses.SelectMany(x => x.Headers).ToList(); + var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList(); return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason"); } } diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index a2c2fadd..eaa2441f 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -69,15 +69,21 @@ namespace Ocelot.UnitTests.Authorization private void GivenTheAuthServiceReturns(Response expected) { _authService - .Setup(x => x.Authorise(It.IsAny(), It.IsAny>())) + .Setup(x => x.Authorise( + It.IsAny(), + It.IsAny>(), + It.IsAny>())) .Returns(expected); } private void ThenTheAuthServiceIsCalledCorrectly() { _authService - .Verify(x => x.Authorise(It.IsAny(), - It.IsAny>()), Times.Once); + .Verify(x => x.Authorise( + It.IsAny(), + It.IsAny>(), + It.IsAny>()) + , Times.Once); } } } diff --git a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs b/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs index e2990864..11fb341d 100644 --- a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs +++ b/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs @@ -1,7 +1,11 @@ using System.Collections.Generic; using System.Security.Claims; using Ocelot.Authorisation; +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; +using Ocelot.Values; + using Shouldly; using TestStack.BDDfy; using Xunit; @@ -15,6 +19,7 @@ namespace Ocelot.UnitTests.Authorization private readonly ClaimsAuthoriser _claimsAuthoriser; private ClaimsPrincipal _claimsPrincipal; private Dictionary _requirement; + private List _urlPathPlaceholderNameAndValues; private Response _result; public ClaimsAuthoriserTests() @@ -38,6 +43,46 @@ namespace Ocelot.UnitTests.Authorization .BDDfy(); } + [Fact] + public void should_authorize_dynamic_user() + { + this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim("userid", "14"), + })))) + .And(x => x.GivenARouteClaimsRequirement(new Dictionary + { + {"userid", "{userId}"} + })) + .And(x => x.GivenAPlaceHolderNameAndValueList(new List + { + new PlaceholderNameAndValue("{userId}", "14") + })) + .When(x => x.WhenICallTheAuthoriser()) + .Then(x => x.ThenTheUserIsAuthorised()) + .BDDfy(); + } + + [Fact] + public void should_not_authorize_dynamic_user() + { + this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim("userid", "15"), + })))) + .And(x => x.GivenARouteClaimsRequirement(new Dictionary + { + {"userid", "{userId}"} + })) + .And(x => x.GivenAPlaceHolderNameAndValueList(new List + { + new PlaceholderNameAndValue("{userId}", "14") + })) + .When(x => x.WhenICallTheAuthoriser()) + .Then(x => x.ThenTheUserIsntAuthorised()) + .BDDfy(); + } + [Fact] public void should_authorise_user_multiple_claims_of_same_type() { @@ -78,9 +123,14 @@ namespace Ocelot.UnitTests.Authorization _requirement = requirement; } + private void GivenAPlaceHolderNameAndValueList(List urlPathPlaceholderNameAndValues) + { + _urlPathPlaceholderNameAndValues = urlPathPlaceholderNameAndValues; + } + private void WhenICallTheAuthoriser() { - _result = _claimsAuthoriser.Authorise(_claimsPrincipal, _requirement); + _result = _claimsAuthoriser.Authorise(_claimsPrincipal, _requirement, _urlPathPlaceholderNameAndValues); } private void ThenTheUserIsAuthorised() diff --git a/test/Ocelot.UnitTests/Cache/CacheKeyGeneratorTests.cs b/test/Ocelot.UnitTests/Cache/CacheKeyGeneratorTests.cs new file mode 100644 index 00000000..362fcc9a --- /dev/null +++ b/test/Ocelot.UnitTests/Cache/CacheKeyGeneratorTests.cs @@ -0,0 +1,34 @@ +using System.Net.Http; +using Microsoft.AspNetCore.Http; +using Ocelot.Cache; +using Ocelot.Middleware; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Cache { + public class CacheKeyGeneratorTests { + private readonly ICacheKeyGenerator _cacheKeyGenerator; + private readonly DownstreamContext _downstreamContext; + + public CacheKeyGeneratorTests() { + _cacheKeyGenerator = new CacheKeyGenerator(); + _cacheKeyGenerator = new CacheKeyGenerator(); + _downstreamContext = new DownstreamContext(new DefaultHttpContext()) { + DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")) + }; + } + + [Fact] + public void should_generate_cache_key_from_context() { + this.Given(x => x.GivenCacheKeyFromContext(_downstreamContext)) + .BDDfy(); + } + + private void GivenCacheKeyFromContext(DownstreamContext context) { + string generatedCacheKey = _cacheKeyGenerator.GenerateRequestCacheKey(context); + string cachekey = MD5Helper.GenerateMd5("GET-https://some.url/blah?abcd=123"); + generatedCacheKey.ShouldBe(cachekey); + } + } +} diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 6f32739b..62d972f9 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -27,14 +27,17 @@ private OutputCacheMiddleware _middleware; private readonly DownstreamContext _downstreamContext; private readonly OcelotRequestDelegate _next; + private readonly ICacheKeyGenerator _cacheKeyGenerator; private CachedResponse _response; + public OutputCacheMiddlewareTests() { _cache = new Mock>(); _downstreamContext = new DownstreamContext(new DefaultHttpContext()); _loggerFactory = new Mock(); _logger = new Mock(); + _cacheKeyGenerator = new CacheKeyGenerator(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => Task.CompletedTask; _downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")); @@ -89,7 +92,7 @@ private void WhenICallTheMiddleware() { - _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cache.Object); + _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cache.Object, _cacheKeyGenerator); _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); } diff --git a/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs b/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs index 424c8efd..853354ef 100644 --- a/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs +++ b/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs @@ -23,6 +23,7 @@ public class OutputCacheMiddlewareRealCacheTests { private readonly IOcelotCache _cacheManager; + private readonly ICacheKeyGenerator _cacheKeyGenerator; private readonly OutputCacheMiddleware _middleware; private readonly DownstreamContext _downstreamContext; private OcelotRequestDelegate _next; @@ -32,17 +33,18 @@ public OutputCacheMiddlewareRealCacheTests() { _loggerFactory = new Mock(); - _logger = new Mock(); + _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); var cacheManagerOutputCache = CacheFactory.Build("OcelotOutputCache", x => { x.WithDictionaryHandle(); }); _cacheManager = new OcelotCacheManagerCache(cacheManagerOutputCache); + _cacheKeyGenerator = new CacheKeyGenerator(); _downstreamContext = new DownstreamContext(new DefaultHttpContext()); _downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")); _next = context => Task.CompletedTask; - _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager); + _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _cacheKeyGenerator); } [Fact] @@ -69,7 +71,8 @@ private void ThenTheContentTypeHeaderIsCached() { - var result = _cacheManager.Get("GET-https://some.url/blah?abcd=123", "kanken"); + string cacheKey = MD5Helper.GenerateMd5("GET-https://some.url/blah?abcd=123"); + var result = _cacheManager.Get(cacheKey, "kanken"); var header = result.ContentHeaders["Content-Type"]; header.First().ShouldBe("application/json"); } diff --git a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs index c7ffbb05..a77d7c60 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs @@ -1023,7 +1023,7 @@ Host = "bb.co.uk" } }, - UpstreamHost = "host1" + UpstreamHost = "host2" } } })) diff --git a/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs index ccf44fcd..d2abded6 100644 --- a/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Kubernetes/KubeServiceDiscoveryProviderTests.cs @@ -1,4 +1,5 @@ -using KubeClient.Models; +using KubeClient; +using KubeClient.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -31,7 +32,7 @@ namespace Ocelot.UnitTests.Kubernetes private readonly Mock _factory; private readonly Mock _logger; private string _receivedToken; - private readonly IKubeApiClientFactory _clientFactory; + private readonly IKubeApiClient _clientFactory; public KubeServiceDiscoveryProviderTests() { @@ -42,15 +43,20 @@ namespace Ocelot.UnitTests.Kubernetes _fakekubeServiceDiscoveryUrl = $"http://{_kubeHost}:{_port}"; _serviceEntries = new ServiceV1(); _factory = new Mock(); - _clientFactory = new KubeApiClientFactory(); + + var option = new KubeClientOptions + { + ApiEndPoint = new Uri(_fakekubeServiceDiscoveryUrl), + AccessToken = "txpc696iUhbVoudg164r93CxDTrKRVWG", + AuthStrategy = KubeClient.KubeAuthStrategy.BearerToken, + AllowInsecure = true + }; + + _clientFactory = KubeApiClient.Create(option); _logger = new Mock(); _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); var config = new KubeRegistryConfiguration() { - ApiEndPoint = new Uri(_fakekubeServiceDiscoveryUrl), - AccessToken = "txpc696iUhbVoudg164r93CxDTrKRVWG", - AllowInsecure = true, - AuthStrategy = KubeClient.KubeAuthStrategy.BearerToken, KeyOfServiceInK8s = _serviceName, KubeNamespace = _namespaces }; diff --git a/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs index daef2a2c..4ebb592c 100644 --- a/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs +++ b/test/Ocelot.UnitTests/Middleware/UserDefinedResponseAggregatorTests.cs @@ -140,12 +140,12 @@ namespace Ocelot.UnitTests.Middleware public class TestDefinedAggregator : IDefinedAggregator { - public async Task Aggregate(List responses) + public async Task Aggregate(List responses) { - var tom = await responses[0].Content.ReadAsStringAsync(); - var laura = await responses[1].Content.ReadAsStringAsync(); + var tom = await responses[0].DownstreamResponse.Content.ReadAsStringAsync(); + var laura = await responses[1].DownstreamResponse.Content.ReadAsStringAsync(); var content = $"{tom}, {laura}"; - var headers = responses.SelectMany(x => x.Headers).ToList(); + var headers = responses.SelectMany(x => x.DownstreamResponse.Headers).ToList(); return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers, "some reason"); } } diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index ae11a695..f4222635 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -19,6 +19,10 @@ True + + + +