diff --git a/build.cake b/build.cake index eddaffc0..184b1faa 100644 --- a/build.cake +++ b/build.cake @@ -92,7 +92,7 @@ Task("Version") if (AppVeyor.IsRunningOnAppVeyor) { Information("Persisting version number..."); - PersistVersion(nugetVersion); + PersistVersion(committedVersion, nugetVersion); buildVersion = nugetVersion; } else @@ -229,7 +229,7 @@ Task("CreatePackages") EnsureDirectoryExists(packagesDir); CopyFiles("./src/**/Ocelot.*.nupkg", packagesDir); - GenerateReleaseNotes(); + GenerateReleaseNotes(releaseNotesFile); System.IO.File.WriteAllLines(artifactsFile, new[]{ "nuget:Ocelot." + buildVersion + ".nupkg", @@ -251,9 +251,9 @@ Task("ReleasePackagesToUnstableFeed") .IsDependentOn("CreatePackages") .Does(() => { - if (ShouldPublishToUnstableFeed()) + if (ShouldPublishToUnstableFeed(nugetFeedUnstableBranchFilter, versioning.BranchName)) { - PublishPackages(nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl); + PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl); } }); @@ -306,7 +306,7 @@ Task("ReleasePackagesToStableFeed") .IsDependentOn("DownloadGitHubReleaseArtifacts") .Does(() => { - PublishPackages(nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); + PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); }); Task("Release") @@ -326,9 +326,9 @@ private GitVersion GetNuGetVersionForCommit() } /// Updates project version in all of our projects -private void PersistVersion(string version) +private void PersistVersion(string committedVersion, string newVersion) { - Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, version)); + Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); var projectFiles = GetFiles("./**/*.csproj"); @@ -339,24 +339,30 @@ private void PersistVersion(string version) Information(string.Format("Updating {0}...", file)); var updatedProjectFile = System.IO.File.ReadAllText(file) - .Replace(committedVersion, version); + .Replace(committedVersion, newVersion); System.IO.File.WriteAllText(file, updatedProjectFile); } } /// generates release notes based on issues closed in GitHub since the last release -private void GenerateReleaseNotes() +private void GenerateReleaseNotes(ConvertableFilePath file) { - Information("Generating release notes at " + releaseNotesFile); + if(!IsRunningOnWindows()) + { + Warning("We are not running on Windows so we cannot generate release notes."); + return; + } + + Information("Generating release notes at " + file); var releaseNotesExitCode = StartProcess( @"tools/GitReleaseNotes/tools/gitreleasenotes.exe", - new ProcessSettings { Arguments = ". /o " + releaseNotesFile }); + new ProcessSettings { Arguments = ". /o " + file }); - if (string.IsNullOrEmpty(System.IO.File.ReadAllText(releaseNotesFile))) + if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file))) { - System.IO.File.WriteAllText(releaseNotesFile, "No issues closed since last release"); + System.IO.File.WriteAllText(file, "No issues closed since last release"); } if (releaseNotesExitCode != 0) @@ -366,7 +372,7 @@ private void GenerateReleaseNotes() } /// Publishes code and symbols packages to nuget feed, based on contents of artifacts file -private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbolFeedUrl) +private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) { var artifacts = System.IO.File .ReadAllLines(artifactsFile) @@ -375,6 +381,8 @@ private void PublishPackages(string feedApiKey, string codeFeedUrl, string symbo var codePackage = packagesDir + File(artifacts["nuget"]); + Information("Pushing package " + codePackage); + NuGetPush( codePackage, new NuGetPushSettings { @@ -401,17 +409,17 @@ private string GetResource(string url) } } -private bool ShouldPublishToUnstableFeed() +private bool ShouldPublishToUnstableFeed(string filter, string branchName) { - var regex = new System.Text.RegularExpressions.Regex(nugetFeedUnstableBranchFilter); - var publish = regex.IsMatch(versioning.BranchName); + var regex = new System.Text.RegularExpressions.Regex(filter); + var publish = regex.IsMatch(branchName); if (publish) { - Information("Branch " + versioning.BranchName + " will be published to the unstable feed"); + Information("Branch " + branchName + " will be published to the unstable feed"); } else { - Information("Branch " + versioning.BranchName + " will not be published to the unstable feed"); + Information("Branch " + branchName + " will not be published to the unstable feed"); } return publish; } \ No newline at end of file diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs index 96f7acd6..13c18bb3 100644 --- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs +++ b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs @@ -7,7 +7,7 @@ namespace Ocelot.Authorisation { using Infrastructure.Claims.Parser; - public class ClaimsAuthoriser : IAuthoriser + public class ClaimsAuthoriser : IClaimsAuthoriser { private readonly IClaimsParser _claimsParser; diff --git a/src/Ocelot/Authorisation/IAuthoriser.cs b/src/Ocelot/Authorisation/IClaimsAuthoriser.cs similarity index 67% rename from src/Ocelot/Authorisation/IAuthoriser.cs rename to src/Ocelot/Authorisation/IClaimsAuthoriser.cs index 08a7307f..0288403a 100644 --- a/src/Ocelot/Authorisation/IAuthoriser.cs +++ b/src/Ocelot/Authorisation/IClaimsAuthoriser.cs @@ -5,9 +5,8 @@ namespace Ocelot.Authorisation { using System.Collections.Generic; - public interface IAuthoriser + public interface IClaimsAuthoriser { - Response Authorise(ClaimsPrincipal claimsPrincipal, - Dictionary routeClaimsRequirement); + Response Authorise(ClaimsPrincipal claimsPrincipal, Dictionary routeClaimsRequirement); } } diff --git a/src/Ocelot/Authorisation/IScopesAuthoriser.cs b/src/Ocelot/Authorisation/IScopesAuthoriser.cs new file mode 100644 index 00000000..62b7bf93 --- /dev/null +++ b/src/Ocelot/Authorisation/IScopesAuthoriser.cs @@ -0,0 +1,12 @@ +using System.Security.Claims; +using Ocelot.Responses; + +namespace Ocelot.Authorisation +{ + using System.Collections.Generic; + + public interface IScopesAuthoriser + { + Response Authorise(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes); + } +} diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index a86643a4..bfcd5f8d 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -1,6 +1,7 @@ using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Responses; +using Ocelot.Configuration; namespace Ocelot.Authorisation.Middleware { @@ -13,17 +14,20 @@ namespace Ocelot.Authorisation.Middleware public class AuthorisationMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; - private readonly IAuthoriser _authoriser; + private readonly IClaimsAuthoriser _claimsAuthoriser; + private readonly IScopesAuthoriser _scopesAuthoriser; private readonly IOcelotLogger _logger; public AuthorisationMiddleware(RequestDelegate next, IRequestScopedDataRepository requestScopedDataRepository, - IAuthoriser authoriser, + IClaimsAuthoriser claimsAuthoriser, + IScopesAuthoriser scopesAuthoriser, IOcelotLoggerFactory loggerFactory) : base(requestScopedDataRepository) { _next = next; - _authoriser = authoriser; + _claimsAuthoriser = claimsAuthoriser; + _scopesAuthoriser = scopesAuthoriser; _logger = loggerFactory.CreateLogger(); } @@ -31,11 +35,41 @@ namespace Ocelot.Authorisation.Middleware { _logger.LogDebug("started authorisation"); - if (DownstreamRoute.ReRoute.IsAuthorised) + if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) + { + _logger.LogDebug("route is authenticated scopes must be checked"); + + var authorised = _scopesAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.AuthenticationOptions.AllowedScopes); + + if (authorised.IsError) + { + _logger.LogDebug("error authorising user scopes"); + + SetPipelineError(authorised.Errors); + return; + } + + if (IsAuthorised(authorised)) + { + _logger.LogDebug("user scopes is authorised calling next authorisation checks"); + } + else + { + _logger.LogDebug("user scopes is not authorised setting pipeline error"); + + SetPipelineError(new List + { + new UnauthorisedError( + $"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}") + }); + } + } + + if (IsAuthorisedRoute(DownstreamRoute.ReRoute)) { _logger.LogDebug("route is authorised"); - var authorised = _authoriser.Authorise(context.User, DownstreamRoute.ReRoute.RouteClaimsRequirement); + var authorised = _claimsAuthoriser.Authorise(context.User, DownstreamRoute.ReRoute.RouteClaimsRequirement); if (authorised.IsError) { @@ -78,5 +112,15 @@ namespace Ocelot.Authorisation.Middleware { return authorised.Data; } + + private static bool IsAuthenticatedRoute(ReRoute reRoute) + { + return reRoute.IsAuthenticated; + } + + private static bool IsAuthorisedRoute(ReRoute reRoute) + { + return reRoute.IsAuthorised; + } } } diff --git a/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs b/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs new file mode 100644 index 00000000..5460cb90 --- /dev/null +++ b/src/Ocelot/Authorisation/ScopeNotAuthorisedError.cs @@ -0,0 +1,12 @@ +using Ocelot.Errors; + +namespace Ocelot.Authorisation +{ + public class ScopeNotAuthorisedError : Error + { + public ScopeNotAuthorisedError(string message) + : base(message, OcelotErrorCode.ScopeNotAuthorisedError) + { + } + } +} diff --git a/src/Ocelot/Authorisation/ScopesAuthoriser.cs b/src/Ocelot/Authorisation/ScopesAuthoriser.cs new file mode 100644 index 00000000..6f32d6c3 --- /dev/null +++ b/src/Ocelot/Authorisation/ScopesAuthoriser.cs @@ -0,0 +1,51 @@ +using IdentityModel; +using Ocelot.Errors; +using Ocelot.Responses; +using System.Collections.Generic; +using System.Security.Claims; +using System.Linq; + +namespace Ocelot.Authorisation +{ + using Infrastructure.Claims.Parser; + + public class ScopesAuthoriser : IScopesAuthoriser + { + private readonly IClaimsParser _claimsParser; + + public ScopesAuthoriser(IClaimsParser claimsParser) + { + _claimsParser = claimsParser; + } + + public Response Authorise(ClaimsPrincipal claimsPrincipal, List routeAllowedScopes) + { + if (routeAllowedScopes == null || routeAllowedScopes.Count == 0) + { + return new OkResponse(true); + } + + var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, JwtClaimTypes.Scope); + + if (values.IsError) + { + return new ErrorResponse(values.Errors); + } + + var userScopes = values.Data; + + List matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList(); + + if (matchesScopes == null || matchesScopes.Count == 0) + { + return new ErrorResponse(new List + { + new ScopeNotAuthorisedError( + $"no one user scope: '{string.Join(",", userScopes)}' match with some allowed scope: '{string.Join(",", routeAllowedScopes)}'") + }); + } + + return new OkResponse(true); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 30d9d87a..852c4870 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net.Http; using Ocelot.Values; +using System.Linq; namespace Ocelot.Configuration.Builder { @@ -11,7 +12,7 @@ namespace Ocelot.Configuration.Builder private string _downstreamPathTemplate; private string _upstreamTemplate; private string _upstreamTemplatePattern; - private string _upstreamHttpMethod; + private List _upstreamHttpMethod; private bool _isAuthenticated; private List _configHeaderExtractorProperties; private List _claimToClaims; @@ -66,11 +67,13 @@ namespace Ocelot.Configuration.Builder _upstreamTemplatePattern = input; return this; } - public ReRouteBuilder WithUpstreamHttpMethod(string input) + + public ReRouteBuilder WithUpstreamHttpMethod(List input) { - _upstreamHttpMethod = input; + _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); return this; } + public ReRouteBuilder WithIsAuthenticated(bool input) { _isAuthenticated = input; @@ -143,7 +146,6 @@ namespace Ocelot.Configuration.Builder return this; } - public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey) { _loadBalancerKey = loadBalancerKey; @@ -180,7 +182,7 @@ namespace Ocelot.Configuration.Builder return new ReRoute( new PathTemplate(_downstreamPathTemplate), new PathTemplate(_upstreamTemplate), - new HttpMethod(_upstreamHttpMethod), + _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, _authenticationOptions, diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index a2ebb709..c44edd08 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -1,191 +1,191 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Parser; -using Ocelot.Configuration.Validator; -using Ocelot.LoadBalancer.LoadBalancers; -using Ocelot.Logging; -using Ocelot.Requester.QoS; -using Ocelot.Responses; -using Ocelot.Utilities; - -namespace Ocelot.Configuration.Creator -{ - /// - /// Register as singleton - /// - public class FileOcelotConfigurationCreator : IOcelotConfigurationCreator - { - private readonly IOptions _options; - private readonly IConfigurationValidator _configurationValidator; - private readonly IOcelotLogger _logger; - private readonly ILoadBalancerFactory _loadBalanceFactory; - private readonly ILoadBalancerHouse _loadBalancerHouse; - private readonly IQoSProviderFactory _qoSProviderFactory; - private readonly IQosProviderHouse _qosProviderHouse; - private readonly IClaimsToThingCreator _claimsToThingCreator; - private readonly IAuthenticationOptionsCreator _authOptionsCreator; - private IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; - private IRequestIdKeyCreator _requestIdKeyCreator; - private IServiceProviderConfigurationCreator _serviceProviderConfigCreator; - private IQoSOptionsCreator _qosOptionsCreator; - private IReRouteOptionsCreator _fileReRouteOptionsCreator; - private IRateLimitOptionsCreator _rateLimitOptionsCreator; - - public FileOcelotConfigurationCreator( - IOptions options, +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Ocelot.Configuration.Builder; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Parser; +using Ocelot.Configuration.Validator; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.Logging; +using Ocelot.Requester.QoS; +using Ocelot.Responses; +using Ocelot.Utilities; + +namespace Ocelot.Configuration.Creator +{ + /// + /// Register as singleton + /// + public class FileOcelotConfigurationCreator : IOcelotConfigurationCreator + { + private readonly IOptions _options; + private readonly IConfigurationValidator _configurationValidator; + private readonly IOcelotLogger _logger; + private readonly ILoadBalancerFactory _loadBalanceFactory; + private readonly ILoadBalancerHouse _loadBalancerHouse; + private readonly IQoSProviderFactory _qoSProviderFactory; + private readonly IQosProviderHouse _qosProviderHouse; + private readonly IClaimsToThingCreator _claimsToThingCreator; + private readonly IAuthenticationOptionsCreator _authOptionsCreator; + private IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; + private IRequestIdKeyCreator _requestIdKeyCreator; + private IServiceProviderConfigurationCreator _serviceProviderConfigCreator; + private IQoSOptionsCreator _qosOptionsCreator; + private IReRouteOptionsCreator _fileReRouteOptionsCreator; + private IRateLimitOptionsCreator _rateLimitOptionsCreator; + + public FileOcelotConfigurationCreator( + IOptions options, IConfigurationValidator configurationValidator, - IOcelotLoggerFactory loggerFactory, - ILoadBalancerFactory loadBalancerFactory, - ILoadBalancerHouse loadBalancerHouse, - IQoSProviderFactory qoSProviderFactory, - IQosProviderHouse qosProviderHouse, - IClaimsToThingCreator claimsToThingCreator, - IAuthenticationOptionsCreator authOptionsCreator, - IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, - IRequestIdKeyCreator requestIdKeyCreator, - IServiceProviderConfigurationCreator serviceProviderConfigCreator, - IQoSOptionsCreator qosOptionsCreator, - IReRouteOptionsCreator fileReRouteOptionsCreator, - IRateLimitOptionsCreator rateLimitOptionsCreator - ) - { - _rateLimitOptionsCreator = rateLimitOptionsCreator; - _requestIdKeyCreator = requestIdKeyCreator; - _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; - _authOptionsCreator = authOptionsCreator; - _loadBalanceFactory = loadBalancerFactory; - _loadBalancerHouse = loadBalancerHouse; - _qoSProviderFactory = qoSProviderFactory; - _qosProviderHouse = qosProviderHouse; - _options = options; - _configurationValidator = configurationValidator; - _logger = loggerFactory.CreateLogger(); - _claimsToThingCreator = claimsToThingCreator; - _serviceProviderConfigCreator = serviceProviderConfigCreator; - _qosOptionsCreator = qosOptionsCreator; - _fileReRouteOptionsCreator = fileReRouteOptionsCreator; - } - - public async Task> Create() - { - var config = await SetUpConfiguration(_options.Value); - - return new OkResponse(config); - } - - public async Task> Create(FileConfiguration fileConfiguration) - { - var config = await SetUpConfiguration(fileConfiguration); - - return new OkResponse(config); - } - - private async Task SetUpConfiguration(FileConfiguration fileConfiguration) - { - var response = _configurationValidator.IsValid(fileConfiguration); - - if (response.Data.IsError) - { - var errorBuilder = new StringBuilder(); - - foreach (var error in response.Errors) - { - errorBuilder.AppendLine(error.Message); - } - - throw new Exception($"Unable to start Ocelot..configuration, errors were {errorBuilder}"); - } - - var reRoutes = new List(); - - foreach (var reRoute in fileConfiguration.ReRoutes) - { - var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration); - reRoutes.Add(ocelotReRoute); - } - - return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath); - } - - private async Task SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) - { - var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); - - var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration); - - var reRouteKey = CreateReRouteKey(fileReRoute); - - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); - - var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration); - - var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); - - var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest); - - var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest); - - var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); - - var qosOptions = _qosOptionsCreator.Create(fileReRoute); - - var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting); - - var reRoute = new ReRouteBuilder() - .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) - .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamTemplatePattern(upstreamTemplatePattern) - .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated) - .WithAuthenticationOptions(authOptionsForRoute) - .WithClaimsToHeaders(claimsToHeaders) - .WithClaimsToClaims(claimsToClaims) - .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) - .WithIsAuthorised(fileReRouteOptions.IsAuthorised) - .WithClaimsToQueries(claimsToQueries) - .WithRequestIdKey(requestIdKey) - .WithIsCached(fileReRouteOptions.IsCached) - .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) - .WithDownstreamScheme(fileReRoute.DownstreamScheme) - .WithLoadBalancer(fileReRoute.LoadBalancer) - .WithDownstreamHost(fileReRoute.DownstreamHost) - .WithDownstreamPort(fileReRoute.DownstreamPort) - .WithLoadBalancerKey(reRouteKey) - .WithServiceProviderConfiguraion(serviceProviderConfiguration) - .WithIsQos(fileReRouteOptions.IsQos) - .WithQosOptions(qosOptions) - .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) - .WithRateLimitOptions(rateLimitOption) - .Build(); - - await SetupLoadBalancer(reRoute); - SetupQosProvider(reRoute); - return reRoute; - } - - private string CreateReRouteKey(FileReRoute fileReRoute) - { - //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain - var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; - return loadBalancerKey; - } - - private async Task SetupLoadBalancer(ReRoute reRoute) - { - var loadBalancer = await _loadBalanceFactory.Get(reRoute); - _loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer); - } - - private void SetupQosProvider(ReRoute reRoute) - { - var loadBalancer = _qoSProviderFactory.Get(reRoute); - _qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer); - } - } + IOcelotLoggerFactory loggerFactory, + ILoadBalancerFactory loadBalancerFactory, + ILoadBalancerHouse loadBalancerHouse, + IQoSProviderFactory qoSProviderFactory, + IQosProviderHouse qosProviderHouse, + IClaimsToThingCreator claimsToThingCreator, + IAuthenticationOptionsCreator authOptionsCreator, + IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, + IRequestIdKeyCreator requestIdKeyCreator, + IServiceProviderConfigurationCreator serviceProviderConfigCreator, + IQoSOptionsCreator qosOptionsCreator, + IReRouteOptionsCreator fileReRouteOptionsCreator, + IRateLimitOptionsCreator rateLimitOptionsCreator + ) + { + _rateLimitOptionsCreator = rateLimitOptionsCreator; + _requestIdKeyCreator = requestIdKeyCreator; + _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; + _authOptionsCreator = authOptionsCreator; + _loadBalanceFactory = loadBalancerFactory; + _loadBalancerHouse = loadBalancerHouse; + _qoSProviderFactory = qoSProviderFactory; + _qosProviderHouse = qosProviderHouse; + _options = options; + _configurationValidator = configurationValidator; + _logger = loggerFactory.CreateLogger(); + _claimsToThingCreator = claimsToThingCreator; + _serviceProviderConfigCreator = serviceProviderConfigCreator; + _qosOptionsCreator = qosOptionsCreator; + _fileReRouteOptionsCreator = fileReRouteOptionsCreator; + } + + public async Task> Create() + { + var config = await SetUpConfiguration(_options.Value); + + return new OkResponse(config); + } + + public async Task> Create(FileConfiguration fileConfiguration) + { + var config = await SetUpConfiguration(fileConfiguration); + + return new OkResponse(config); + } + + private async Task SetUpConfiguration(FileConfiguration fileConfiguration) + { + var response = _configurationValidator.IsValid(fileConfiguration); + + if (response.Data.IsError) + { + var errorBuilder = new StringBuilder(); + + foreach (var error in response.Errors) + { + errorBuilder.AppendLine(error.Message); + } + + throw new Exception($"Unable to start Ocelot..configuration, errors were {errorBuilder}"); + } + + var reRoutes = new List(); + + foreach (var reRoute in fileConfiguration.ReRoutes) + { + var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration); + reRoutes.Add(ocelotReRoute); + } + + return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath); + } + + private async Task SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + { + var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); + + var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration); + + var reRouteKey = CreateReRouteKey(fileReRoute); + + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); + + var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration); + + var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); + + var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest); + + var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest); + + var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); + + var qosOptions = _qosOptionsCreator.Create(fileReRoute); + + var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting); + + var reRoute = new ReRouteBuilder() + .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) + .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) + .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) + .WithUpstreamTemplatePattern(upstreamTemplatePattern) + .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated) + .WithAuthenticationOptions(authOptionsForRoute) + .WithClaimsToHeaders(claimsToHeaders) + .WithClaimsToClaims(claimsToClaims) + .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) + .WithIsAuthorised(fileReRouteOptions.IsAuthorised) + .WithClaimsToQueries(claimsToQueries) + .WithRequestIdKey(requestIdKey) + .WithIsCached(fileReRouteOptions.IsCached) + .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) + .WithDownstreamScheme(fileReRoute.DownstreamScheme) + .WithLoadBalancer(fileReRoute.LoadBalancer) + .WithDownstreamHost(fileReRoute.DownstreamHost) + .WithDownstreamPort(fileReRoute.DownstreamPort) + .WithLoadBalancerKey(reRouteKey) + .WithServiceProviderConfiguraion(serviceProviderConfiguration) + .WithIsQos(fileReRouteOptions.IsQos) + .WithQosOptions(qosOptions) + .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) + .WithRateLimitOptions(rateLimitOption) + .Build(); + + await SetupLoadBalancer(reRoute); + SetupQosProvider(reRoute); + return reRoute; + } + + private string CreateReRouteKey(FileReRoute fileReRoute) + { + //note - not sure if this is the correct key, but this is probably the only unique key i can think of given my poor brain + var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}{fileReRoute.UpstreamHttpMethod}"; + return loadBalancerKey; + } + + private async Task SetupLoadBalancer(ReRoute reRoute) + { + var loadBalancer = await _loadBalanceFactory.Get(reRoute); + _loadBalancerHouse.Add(reRoute.ReRouteKey, loadBalancer); + } + + private void SetupQosProvider(ReRoute reRoute) + { + var loadBalancer = _qoSProviderFactory.Get(reRoute); + _qosProviderHouse.Add(reRoute.ReRouteKey, loadBalancer); + } + } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index 07562653..59483aee 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -6,6 +6,7 @@ namespace Ocelot.Configuration.File { public FileReRoute() { + UpstreamHttpMethod = new List(); AddHeadersToRequest = new Dictionary(); AddClaimsToRequest = new Dictionary(); RouteClaimsRequirement = new Dictionary(); @@ -18,7 +19,7 @@ namespace Ocelot.Configuration.File public string DownstreamPathTemplate { get; set; } public string UpstreamPathTemplate { get; set; } - public string UpstreamHttpMethod { get; set; } + public List UpstreamHttpMethod { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; } public Dictionary AddHeadersToRequest { get; set; } public Dictionary AddClaimsToRequest { get; set; } diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index bba8c925..a6673d09 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -8,7 +8,7 @@ namespace Ocelot.Configuration { public ReRoute(PathTemplate downstreamPathTemplate, PathTemplate upstreamPathTemplate, - HttpMethod upstreamHttpMethod, + List upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, @@ -64,7 +64,7 @@ namespace Ocelot.Configuration public PathTemplate DownstreamPathTemplate { get; private set; } public PathTemplate UpstreamPathTemplate { get; private set; } public string UpstreamTemplatePattern { get; private set; } - public HttpMethod UpstreamHttpMethod { get; private set; } + public List UpstreamHttpMethod { get; private set; } public bool IsAuthenticated { get; private set; } public bool IsAuthorised { get; private set; } public AuthenticationOptions AuthenticationOptions { get; private set; } diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs index 4bd04917..54a23f25 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationValidator.cs @@ -12,7 +12,7 @@ namespace Ocelot.Configuration.Validator { public Response IsValid(FileConfiguration configuration) { - var result = CheckForDupliateReRoutes(configuration); + var result = CheckForDuplicateReRoutes(configuration); if (result.IsError) { @@ -97,25 +97,41 @@ namespace Ocelot.Configuration.Validator return new ConfigurationValidationResult(false, errors); } - private ConfigurationValidationResult CheckForDupliateReRoutes(FileConfiguration configuration) - { - var hasDupes = configuration.ReRoutes - .GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }).Any(x => x.Skip(1).Any()); + private ConfigurationValidationResult CheckForDuplicateReRoutes(FileConfiguration configuration) + { + var duplicatedUpstreamPathTemplates = new List(); - if (!hasDupes) + var distinctUpstreamPathTemplates = configuration.ReRoutes.Select(x => x.UpstreamPathTemplate).Distinct(); + + foreach (string upstreamPathTemplate in distinctUpstreamPathTemplates) + { + var reRoutesWithUpstreamPathTemplate = configuration.ReRoutes.Where(x => x.UpstreamPathTemplate == upstreamPathTemplate); + + var hasEmptyListToAllowAllHttpVerbs = reRoutesWithUpstreamPathTemplate.Where(x => x.UpstreamHttpMethod.Count() == 0).Any(); + var hasDuplicateEmptyListToAllowAllHttpVerbs = reRoutesWithUpstreamPathTemplate.Where(x => x.UpstreamHttpMethod.Count() == 0).Count() > 1; + var hasSpecificHttpVerbs = reRoutesWithUpstreamPathTemplate.Where(x => x.UpstreamHttpMethod.Count() > 0).Any(); + var hasDuplicateSpecificHttpVerbs = reRoutesWithUpstreamPathTemplate.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any(); + + if (hasDuplicateEmptyListToAllowAllHttpVerbs || hasDuplicateSpecificHttpVerbs || (hasEmptyListToAllowAllHttpVerbs && hasSpecificHttpVerbs)) + { + duplicatedUpstreamPathTemplates.Add(upstreamPathTemplate); + } + } + + if (duplicatedUpstreamPathTemplates.Count() == 0) { return new ConfigurationValidationResult(false); } + else + { + var errors = duplicatedUpstreamPathTemplates + .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d))) + .Cast() + .ToList(); - var dupes = configuration.ReRoutes.GroupBy(x => new { x.UpstreamPathTemplate, x.UpstreamHttpMethod }) - .Where(x => x.Skip(1).Any()); + return new ConfigurationValidationResult(true, errors); + } - var errors = dupes - .Select(d => new DownstreamPathTemplateAlreadyUsedError(string.Format("Duplicate DownstreamPath: {0}", d.Key.UpstreamPathTemplate))) - .Cast() - .ToList(); - - return new ConfigurationValidationResult(true, errors); } private ConfigurationValidationResult CheckForReRoutesRateLimitOptions(FileConfiguration configuration) diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index ef022923..d52341fe 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -144,7 +144,8 @@ namespace Ocelot.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index 82a5e7c5..bf0d5a52 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -26,7 +26,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder { var configuration = await _configProvider.Get(); - var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method.ToLower(), upstreamHttpMethod.ToLower(), StringComparison.CurrentCultureIgnoreCase)); + var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower())); foreach (var reRoute in applicableReRoutes) { diff --git a/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs b/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs index 0859f4c0..8e5a2416 100644 --- a/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/UrlMatcher/UrlPathPlaceholderNameAndValueFinder.cs @@ -70,7 +70,7 @@ namespace Ocelot.DownstreamRouteFinder.UrlMatcher private bool CharactersDontMatch(char characterOne, char characterTwo) { - return characterOne != characterTwo; + return char.ToLower(characterOne) != char.ToLower(characterTwo); } private bool ContinueScanningUrl(int counterForUrl, int urlLength) diff --git a/src/Ocelot/Errors/OcelotErrorCode.cs b/src/Ocelot/Errors/OcelotErrorCode.cs index 29e9d57f..817edbd8 100644 --- a/src/Ocelot/Errors/OcelotErrorCode.cs +++ b/src/Ocelot/Errors/OcelotErrorCode.cs @@ -17,6 +17,7 @@ InstructionNotForClaimsError, UnauthorizedError, ClaimValueNotAuthorisedError, + ScopeNotAuthorisedError, UserDoesNotHaveClaimError, DownstreamPathTemplateContainsSchemeError, DownstreamPathNullOrEmptyError, diff --git a/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs b/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs index e37f48fa..f5f3e3b4 100644 --- a/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs +++ b/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs @@ -1,10 +1,10 @@ namespace Ocelot.Infrastructure.Claims.Parser { + using Errors; + using Responses; using System.Collections.Generic; using System.Linq; using System.Security.Claims; - using Errors; - using Responses; public class ClaimsParser : IClaimsParser { @@ -37,6 +37,17 @@ return new OkResponse(value); } + + public Response> GetValuesByClaimType(IEnumerable claims, string claimType) + { + List values = new List(); + + values.AddRange(claims.Where(x => x.Type == claimType).Select(x => x.Value).ToList()); + + return new OkResponse>(values); + } + + private Response GetValue(IEnumerable claims, string key) { var claim = claims.FirstOrDefault(c => c.Type == key); diff --git a/src/Ocelot/Infrastructure/Claims/Parser/IClaimsParser.cs b/src/Ocelot/Infrastructure/Claims/Parser/IClaimsParser.cs index fa94cd22..1f6e65d3 100644 --- a/src/Ocelot/Infrastructure/Claims/Parser/IClaimsParser.cs +++ b/src/Ocelot/Infrastructure/Claims/Parser/IClaimsParser.cs @@ -7,5 +7,6 @@ public interface IClaimsParser { Response GetValue(IEnumerable claims, string key, string delimiter, int index); + Response> GetValuesByClaimType(IEnumerable claims, string claimType); } } \ No newline at end of file diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index d993290e..32cca96e 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -45,7 +45,7 @@ - + @@ -53,7 +53,7 @@ - + diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index a7f747d4..3f85af0c 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -22,9 +22,12 @@ namespace Ocelot.Requester public async Task> GetResponse(Request.Request request) { - var cacheKey = GetCacheKey(request); + var builder = new HttpClientBuilder(); + + var cacheKey = GetCacheKey(request, builder); + + var httpClient = GetHttpClient(cacheKey, builder); - IHttpClient httpClient = GetHttpClient(cacheKey); try { var response = await httpClient.SendAsync(request.HttpRequestMessage); @@ -51,11 +54,10 @@ namespace Ocelot.Requester } - private IHttpClient GetHttpClient(string cacheKey) + private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder) { - var builder = new HttpClientBuilder(); - var httpClient = _cacheHandlers.Get(cacheKey); + if (httpClient == null) { httpClient = builder.Create(); @@ -63,9 +65,16 @@ namespace Ocelot.Requester return httpClient; } - private string GetCacheKey(Request.Request request) + private string GetCacheKey(Request.Request request, IHttpClientBuilder builder) { string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; + + if (request.IsQos) + { + builder.WithQos(request.QosProvider, _logger); + baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; + } + return baseUrl; } } diff --git a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs index 5c1e9fd0..be7c59b9 100644 --- a/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs +++ b/src/Ocelot/Responder/ErrorsToHttpStatusCodeMapper.cs @@ -15,6 +15,7 @@ namespace Ocelot.Responder if (errors.Any(e => e.Code == OcelotErrorCode.UnauthorizedError || e.Code == OcelotErrorCode.ClaimValueNotAuthorisedError + || e.Code == OcelotErrorCode.ScopeNotAuthorisedError || e.Code == OcelotErrorCode.UserDoesNotHaveClaimError || e.Code == OcelotErrorCode.CannotFindClaimError)) { diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 6f1609f6..c0d143b9 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -48,21 +48,21 @@ namespace Ocelot.AcceptanceTests DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Post", + UpstreamHttpMethod = new List { "Post" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) @@ -86,21 +86,21 @@ namespace Ocelot.AcceptanceTests DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Post", + UpstreamHttpMethod = new List { "Post" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) @@ -124,21 +124,21 @@ namespace Ocelot.AcceptanceTests DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) @@ -150,6 +150,45 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + ProviderRootUrl = _identityServerRootUrl, + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); + } + [Fact] public void should_return_201_using_identity_server_access_token() { @@ -164,22 +203,22 @@ namespace Ocelot.AcceptanceTests DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Post", - + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) @@ -205,21 +244,21 @@ namespace Ocelot.AcceptanceTests DownstreamHost = _downstreamServiceHost, DownstreamScheme = _downstreamServiceScheme, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Post", - AuthenticationOptions = new FileAuthenticationOptions + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) @@ -252,7 +291,7 @@ namespace Ocelot.AcceptanceTests _servicebuilder.Start(); } - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType) + private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType) { _identityServerBuilder = new WebHostBuilder() .UseUrls(url) @@ -276,6 +315,32 @@ namespace Ocelot.AcceptanceTests Scopes = new List() { new Scope("api"), + new Scope("api.readOnly"), + new Scope("openid"), + new Scope("offline_access") + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256() + } + }, + UserClaims = new List() + { + "CustomerId", "LocationId" + } + }, + new ApiResource + { + Name = api2Name, + Description = "My second API", + Enabled = true, + DisplayName = "second test", + Scopes = new List() + { + new Scope("api2"), + new Scope("api2.readOnly"), new Scope("openid"), new Scope("offline_access") }, @@ -299,7 +364,7 @@ namespace Ocelot.AcceptanceTests ClientId = "client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, + AllowedScopes = new List { apiName, api2Name, "api.readOnly", "openid", "offline_access" }, AccessTokenType = tokenType, Enabled = true, RequireClientSecret = false diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 3b1609c9..011bb679 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -42,7 +42,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { AllowedScopes = new List(), @@ -99,7 +99,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { AllowedScopes = new List(), @@ -140,6 +140,84 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_return_response_200_using_identity_server_with_allowed_scope() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamHost = "localhost", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List{ "api", "api.readOnly", "openid", "offline_access" }, + Provider = "IdentityServer", + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_403_using_identity_server_with_scope_not_allowed() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamHost = "localhost", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List{ "api", "openid", "offline_access" }, + Provider = "IdentityServer", + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) { _servicebuilder = new WebHostBuilder() @@ -185,6 +263,7 @@ namespace Ocelot.AcceptanceTests Scopes = new List() { new Scope("api"), + new Scope("api.readOnly"), new Scope("openid"), new Scope("offline_access") }, @@ -209,7 +288,7 @@ namespace Ocelot.AcceptanceTests ClientId = "client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, + AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, AccessTokenType = tokenType, Enabled = true, RequireClientSecret = false diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index a8364855..888cf151 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -36,7 +36,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, FileCacheOptions = new FileCacheOptions { TtlSeconds = 100 @@ -72,7 +72,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, FileCacheOptions = new FileCacheOptions { TtlSeconds = 1 diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index 85e8fd68..740bf512 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -35,7 +35,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -62,7 +62,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false, } } @@ -90,7 +90,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, } } @@ -118,7 +118,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/PRODUCTS/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, } } @@ -146,7 +146,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, } } @@ -174,7 +174,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/PRODUCTS/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, } } diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 702ecd0d..88a294a0 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -56,12 +56,12 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { AllowedScopes = new List { - "openid", "offline_access" + "openid", "offline_access", "api" }, Provider = "IdentityServer", ProviderRootUrl = "http://localhost:52888", diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index 2cca7048..a6162c5f 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -56,12 +56,12 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { AllowedScopes = new List { - "openid", "offline_access" + "openid", "offline_access", "api" }, Provider = "IdentityServer", ProviderRootUrl = "http://localhost:57888", diff --git a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs index 959777c6..8416a66d 100644 --- a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs +++ b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs @@ -47,7 +47,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/api/ClientRateLimit", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, RequestIdKey = _steps.RequestIdKey, RateLimitOptions = new FileRateLimitRule() @@ -102,7 +102,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/api/ClientRateLimit", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, RequestIdKey = _steps.RequestIdKey, RateLimitOptions = new FileRateLimitRule() diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index b2dfdc80..0ab4cc6a 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -44,8 +44,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51779, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } }, GlobalConfiguration = new FileGlobalConfiguration() diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 7d33febb..6b45c6f8 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -50,7 +50,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -87,8 +87,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -125,8 +124,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -163,7 +161,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -200,7 +198,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -237,7 +235,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 4adb231f..0faafe84 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -43,7 +43,6 @@ - diff --git a/test/Ocelot.AcceptanceTests/QoSTests.cs b/test/Ocelot.AcceptanceTests/QoSTests.cs index 0eb3d8a4..8825b75b 100644 --- a/test/Ocelot.AcceptanceTests/QoSTests.cs +++ b/test/Ocelot.AcceptanceTests/QoSTests.cs @@ -39,13 +39,14 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions { ExceptionsAllowedBeforeBreaking = 1, TimeoutValue = 500, DurationOfBreak = 1000 - } + }, + } } }; @@ -83,7 +84,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions { ExceptionsAllowedBeforeBreaking = 1, @@ -98,7 +99,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51880, UpstreamPathTemplate = "working", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index 46a5fc13..1c87bc9c 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -38,7 +38,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, RequestIdKey = _steps.RequestIdKey, } } @@ -66,8 +66,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -96,7 +95,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } }, GlobalConfiguration = new FileGlobalConfiguration diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index 868cb39f..86815e98 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -31,7 +31,7 @@ namespace Ocelot.AcceptanceTests { DownstreamPathTemplate = "/", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, DownstreamPort = 53876, DownstreamScheme = "http", DownstreamHost = "localhost" diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 99c11877..b32c4690 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -45,7 +45,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -73,8 +73,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -102,8 +101,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost/", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -131,8 +129,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/products/", - UpstreamHttpMethod = "Get", - + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -160,7 +157,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/products", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -188,7 +185,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions() { ExceptionsAllowedBeforeBreaking = 3, @@ -221,7 +218,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -249,7 +246,7 @@ namespace Ocelot.AcceptanceTests DownstreamPort = 51879, DownstreamScheme = "http", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Post", + UpstreamHttpMethod = new List { "Post" }, } } }; @@ -277,7 +274,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51879, - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -305,7 +302,7 @@ namespace Ocelot.AcceptanceTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; @@ -319,6 +316,62 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_return_response_201_with_simple_url_and_multiple_upstream_http_method() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamHost = "localhost", + DownstreamPort = 51879, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get", "Post" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 201, string.Empty)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenThePostHasContent("postContent")) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_simple_url_and_any_upstream_http_method() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamHost = "localhost", + DownstreamPort = 51879, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List(), + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) { _builder = new WebHostBuilder() diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index fcf75b14..943e8a23 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -69,7 +69,7 @@ namespace Ocelot.AcceptanceTests DownstreamPathTemplate = "/", DownstreamScheme = "http", UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ServiceName = serviceName, LoadBalancer = "LeastConnection", } diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 79a586ac..cb408037 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -199,6 +199,52 @@ namespace Ocelot.AcceptanceTests } } + public void GivenIHaveATokenForApiReadOnlyScope(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api.readOnly"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + public void GivenIHaveATokenForApi2(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api2"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + public void GivenIHaveAnOcelotToken(string adminPath) { var tokenUrl = $"{adminPath}/connect/token"; diff --git a/test/Ocelot.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index 642493bb..2fa2c05c 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -94,7 +94,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 80, DownstreamScheme = "https", DownstreamPathTemplate = "/", - UpstreamHttpMethod = "get", + UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/" }, new FileReRoute() @@ -103,7 +103,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 80, DownstreamScheme = "https", DownstreamPathTemplate = "/", - UpstreamHttpMethod = "get", + UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/test" } } @@ -136,7 +136,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 80, DownstreamScheme = "https", DownstreamPathTemplate = "/", - UpstreamHttpMethod = "get", + UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/" }, new FileReRoute() @@ -145,7 +145,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 80, DownstreamScheme = "https", DownstreamPathTemplate = "/", - UpstreamHttpMethod = "get", + UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/test" } } @@ -165,7 +165,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 80, DownstreamScheme = "http", DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = "get", + UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/" }, new FileReRoute() @@ -174,7 +174,7 @@ namespace Ocelot.IntegrationTests DownstreamPort = 443, DownstreamScheme = "https", DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = "post", + UpstreamHttpMethod = new List { "post" }, UpstreamPathTemplate = "/test" } } diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 18ae6b22..db4b23d2 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -44,7 +44,7 @@ - + diff --git a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs index 191dea47..2512a87d 100644 --- a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs +++ b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs @@ -55,7 +55,7 @@ namespace Ocelot.IntegrationTests DownstreamHost = "localhost", DownstreamPort = 51879, UpstreamPathTemplate = "/", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } } }; diff --git a/test/Ocelot.ManualTest/configuration.json b/test/Ocelot.ManualTest/configuration.json index 9e1f394b..765326cf 100644 --- a/test/Ocelot.ManualTest/configuration.json +++ b/test/Ocelot.ManualTest/configuration.json @@ -1,311 +1,311 @@ { - "ReRoutes": [ - { - "DownstreamPathTemplate": "/", - "DownstreamScheme": "http", - "DownstreamHost": "localhost", - "DownstreamPort": 52876, - "UpstreamPathTemplate": "/identityserverexample", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "AuthenticationOptions": { - "Provider": "IdentityServer", - "ProviderRootUrl": "http://localhost:52888", - "ApiName": "api", - "AllowedScopes": [ - "openid", - "offline_access" - ], - "ApiSecret": "secret" - }, - "AddHeadersToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "AddClaimsToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "AddQueriesToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "RouteClaimsRequirement": { - "UserType": "registered" - }, - "RequestIdKey": "OcRequestId" - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "https", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 443, - "UpstreamPathTemplate": "/posts", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}/comments", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/{postId}/comments", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/comments", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/comments", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts", - "UpstreamHttpMethod": "Post", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": "Put", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": "Patch", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": "Delete", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/api/products", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/products", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/products/{productId}", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/products/{productId}", - "UpstreamHttpMethod": "Get", - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/products", - "DownstreamScheme": "http", - "DownstreamHost": "products20161126090340.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/products", - "UpstreamHttpMethod": "Post", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/api/products/{productId}", - "DownstreamScheme": "http", - "DownstreamHost": "products20161126090340.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/products/{productId}", - "UpstreamHttpMethod": "Put", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/products/{productId}", - "DownstreamScheme": "http", - "DownstreamHost": "products20161126090340.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/products/{productId}", - "UpstreamHttpMethod": "Delete", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/customers", - "DownstreamScheme": "http", - "DownstreamHost": "customers20161126090811.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/customers", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/customers/{customerId}", - "DownstreamScheme": "http", - "DownstreamHost": "customers20161126090811.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/customers/{customerId}", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/customers", - "DownstreamScheme": "http", - "DownstreamHost": "customers20161126090811.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/customers", - "UpstreamHttpMethod": "Post", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/customers/{customerId}", - "DownstreamScheme": "http", - "DownstreamHost": "customers20161126090811.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/customers/{customerId}", - "UpstreamHttpMethod": "Put", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/customers/{customerId}", - "DownstreamScheme": "http", - "DownstreamHost": "customers20161126090811.azurewebsites.net", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/customers/{customerId}", - "UpstreamHttpMethod": "Delete", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "http", - "DownstreamHost": "jsonplaceholder.typicode.com", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/posts/", - "UpstreamHttpMethod": "Get", - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/", - "DownstreamScheme": "http", - "DownstreamHost": "www.bbc.co.uk", - "DownstreamPort": 80, - "UpstreamPathTemplate": "/bbc/", - "UpstreamHttpMethod": "Get" - } - ], + "ReRoutes": [ + { + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHost": "localhost", + "DownstreamPort": 52876, + "UpstreamPathTemplate": "/identityserverexample", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "AuthenticationOptions": { + "Provider": "IdentityServer", + "ProviderRootUrl": "http://localhost:52888", + "ApiName": "api", + "AllowedScopes": [ + "openid", + "offline_access" + ], + "ApiSecret": "secret" + }, + "AddHeadersToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "AddClaimsToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "AddQueriesToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "RouteClaimsRequirement": { + "UserType": "registered" + }, + "RequestIdKey": "OcRequestId" + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "https", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 443, + "UpstreamPathTemplate": "/posts", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}/comments", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/{postId}/comments", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/comments", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/comments", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts", + "UpstreamHttpMethod": [ "Post" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Put" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Patch" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Delete" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/products", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/products/{productId}", + "UpstreamHttpMethod": [ "Get" ], + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/products", + "UpstreamHttpMethod": [ "Post" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/products/{productId}", + "UpstreamHttpMethod": [ "Put" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHost": "products20161126090340.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/products/{productId}", + "UpstreamHttpMethod": [ "Delete" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/customers", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/customers", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/customers/{customerId}", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/customers", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/customers", + "UpstreamHttpMethod": [ "Post" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/customers/{customerId}", + "UpstreamHttpMethod": [ "Put" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/customers/{customerId}", + "DownstreamScheme": "http", + "DownstreamHost": "customers20161126090811.azurewebsites.net", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/customers/{customerId}", + "UpstreamHttpMethod": [ "Delete" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHost": "jsonplaceholder.typicode.com", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/posts/", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHost": "www.bbc.co.uk", + "DownstreamPort": 80, + "UpstreamPathTemplate": "/bbc/", + "UpstreamHttpMethod": [ "Get" ], + } + ], "GlobalConfiguration": { "RequestIdKey": "OcRequestId", diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs index f60eb83c..be709dff 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs @@ -72,7 +72,7 @@ namespace Ocelot.UnitTests.Authentication public void should_call_next_middleware_if_route_is_not_authenticated() { this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder() - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheUserIsAuthenticated()) diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index 35435fc2..9bf15b42 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -25,7 +25,8 @@ namespace Ocelot.UnitTests.Authorization public class AuthorisationMiddlewareTests : IDisposable { private readonly Mock _scopedRepository; - private readonly Mock _authService; + private readonly Mock _authService; + private readonly Mock _authScopesService; private readonly string _url; private readonly TestServer _server; private readonly HttpClient _client; @@ -36,7 +37,8 @@ namespace Ocelot.UnitTests.Authorization { _url = "http://localhost:51879"; _scopedRepository = new Mock(); - _authService = new Mock(); + _authService = new Mock(); + _authScopesService = new Mock(); var builder = new WebHostBuilder() .ConfigureServices(x => @@ -44,6 +46,7 @@ namespace Ocelot.UnitTests.Authorization x.AddSingleton(); x.AddLogging(); x.AddSingleton(_authService.Object); + x.AddSingleton(_authScopesService.Object); x.AddSingleton(_scopedRepository.Object); }) .UseUrls(_url) @@ -66,7 +69,7 @@ namespace Ocelot.UnitTests.Authorization this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List(), new ReRouteBuilder() .WithIsAuthorised(true) - .WithUpstreamHttpMethod("Get") .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .And(x => x.GivenTheAuthServiceReturns(new OkResponse(true))) .When(x => x.WhenICallTheMiddleware()) diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 6537c684..dfc0220f 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -91,7 +91,7 @@ namespace Ocelot.UnitTests.Cache var reRoute = new ReRouteBuilder() .WithIsCached(true) .WithCacheOptions(new CacheOptions(100)) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build(); var downstreamRoute = new DownstreamRoute(new List(), reRoute); diff --git a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs index 90a16b17..d17d7978 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs @@ -72,7 +72,7 @@ namespace Ocelot.UnitTests.Claims { new ClaimToThing("sub", "UserType", "|", 0) }) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 5f8a37ad..bd8c46d3 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -84,7 +84,7 @@ namespace Ocelot.UnitTests.Configuration DownstreamHost = "127.0.0.1", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } }, })) @@ -117,7 +117,7 @@ namespace Ocelot.UnitTests.Configuration DownstreamHost = "127.0.0.1", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions { TimeoutValue = 1, @@ -153,7 +153,7 @@ namespace Ocelot.UnitTests.Configuration DownstreamHost = "127.0.0.1", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } }, })) @@ -181,7 +181,7 @@ namespace Ocelot.UnitTests.Configuration DownstreamHost = "127.0.0.1", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } }, })) @@ -194,7 +194,7 @@ namespace Ocelot.UnitTests.Configuration .WithDownstreamHost("127.0.0.1") .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build() })) .BDDfy(); @@ -215,7 +215,7 @@ namespace Ocelot.UnitTests.Configuration DownstreamScheme = "https", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, } }, })) @@ -228,7 +228,7 @@ namespace Ocelot.UnitTests.Configuration .WithDownstreamScheme("https") .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build() })) .BDDfy(); @@ -248,7 +248,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false, ServiceName = "ProductService" } @@ -270,7 +270,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() .WithUseServiceDiscovery(true) .WithServiceDiscoveryProvider("consul") @@ -296,7 +296,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false, } } @@ -309,7 +309,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() .WithUseServiceDiscovery(false) .Build()) @@ -332,7 +332,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false } } @@ -346,7 +346,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") .Build() })) @@ -367,7 +367,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true } }, @@ -385,7 +385,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithRequestIdKey("blahhhh") .Build() })) @@ -414,7 +414,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithAuthenticationOptions(authenticationOptions) .WithClaimsToHeaders(new List { @@ -431,7 +431,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, AuthenticationOptions = new FileAuthenticationOptions { @@ -482,7 +482,7 @@ namespace Ocelot.UnitTests.Configuration new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithAuthenticationOptions(authenticationOptions) .Build() }; @@ -495,7 +495,7 @@ namespace Ocelot.UnitTests.Configuration { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = "Get", + UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true, AuthenticationOptions = new FileAuthenticationOptions { diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 1ef4cf6b..b65aa095 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -87,7 +87,7 @@ namespace Ocelot.UnitTests.Configuration { new ReRouteBuilder() .WithDownstreamPathTemplate(_downstreamTemplatePath) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build() }; diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index e7718f37..0b3aa149 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -66,7 +66,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 0d272c63..8514786d 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -45,7 +45,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern("someUpstreamPath") .Build() }, string.Empty @@ -58,7 +58,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) @@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern("someUpstreamPath") .Build() }, string.Empty @@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build() ))) .And(x => x.ThenTheUrlMatcherIsNotCalled()) @@ -110,13 +110,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern("") .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") .WithUpstreamPathTemplate("someUpstreamPath") - .WithUpstreamHttpMethod("Post") + .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern("") .Build() }, string.Empty @@ -128,7 +128,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamHttpMethod("Post") + .WithUpstreamHttpMethod(new List { "Post" }) .Build() ))) .BDDfy(); @@ -143,7 +143,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder new ReRouteBuilder() .WithDownstreamPathTemplate("somPath") .WithUpstreamPathTemplate("somePath") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern("somePath") .Build(), }, string.Empty @@ -157,6 +157,95 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder .BDDfy(); } + [Fact] + public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() + { + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamTemplatePattern("") + .Build() + }, string.Empty + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() + { + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamTemplatePattern("") + .Build() + }, string.Empty + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() + { + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate("someUpstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamTemplatePattern("") + .Build() + }, string.Empty + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) { _finder diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index aea1780f..ae322150 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithDownstreamScheme("https") .Build()))) .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index 00445ee6..4939c901 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -29,7 +29,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new DownstreamRoute( new List(), new ReRouteBuilder() - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("")) @@ -44,7 +44,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("/") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) @@ -57,7 +57,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("api") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) @@ -70,7 +70,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("api/") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) @@ -83,7 +83,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("api/product/products/") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) @@ -101,7 +101,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() .WithDownstreamPathTemplate("productservice/products/{productId}/") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) @@ -119,7 +119,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() .WithDownstreamPathTemplate("productservice/products/{productId}/variants") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) @@ -138,7 +138,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) @@ -158,7 +158,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder() .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .When(x => x.WhenIReplaceTheTemplateVariables()) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index 95989688..395d7862 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -76,7 +76,7 @@ namespace Ocelot.UnitTests.Headers { new ClaimToThing("UserId", "Subject", "", 0) }) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index f8bf8f6c..ccf97772 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -4,6 +4,7 @@ using Ocelot.Configuration.Builder; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.ServiceDiscovery; using Shouldly; +using System.Collections.Generic; using TestStack.BDDfy; using Xunit; @@ -29,7 +30,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build()) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenAReRoute(reRoute)) @@ -44,7 +45,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build()) .Build(); @@ -60,7 +61,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("LeastConnection") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build()) .Build(); @@ -76,7 +77,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var reRoute = new ReRouteBuilder() .WithLoadBalancer("RoundRobin") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder().Build()) .Build(); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index a7dfd1ac..65074b67 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) @@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) @@ -106,7 +106,7 @@ namespace Ocelot.UnitTests.LoadBalancer { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index 898563f7..1343df7d 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -74,7 +74,7 @@ namespace Ocelot.UnitTests.QueryStrings { new ClaimToThing("UserId", "Subject", "", 0) }) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index 81be28cc..a38d5a67 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -72,7 +72,7 @@ namespace Ocelot.UnitTests.RateLimit var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions( new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List(), false, "", "", new Ocelot.Configuration.RateLimitRule("1s", 100, 3), 429)) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) @@ -89,7 +89,7 @@ namespace Ocelot.UnitTests.RateLimit var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder().WithEnableRateLimiting(true).WithRateLimitOptions( new Ocelot.Configuration.RateLimitOptions(true, "ClientId", new List() { "ocelotclient2" }, false, "", "", new RateLimitRule( "1s", 100,3),429)) - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 8cec477e..f8563abf 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -76,7 +76,7 @@ namespace Ocelot.UnitTests.Request var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) diff --git a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs index 4521e315..d56bbce6 100644 --- a/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs @@ -78,7 +78,7 @@ namespace Ocelot.UnitTests.RequestId new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); var requestId = Guid.NewGuid().ToString(); @@ -97,7 +97,7 @@ namespace Ocelot.UnitTests.RequestId new ReRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") - .WithUpstreamHttpMethod("Get") + .WithUpstreamHttpMethod(new List { "Get" }) .Build()); this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) diff --git a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs index f4beb0f1..082e7177 100644 --- a/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/QoSProviderFactoryTests.cs @@ -4,6 +4,7 @@ using Ocelot.Configuration.Builder; using Ocelot.Logging; using Ocelot.Requester.QoS; using Shouldly; +using System.Collections.Generic; using TestStack.BDDfy; using Xunit; @@ -31,7 +32,7 @@ namespace Ocelot.UnitTests.Requester public void should_return_no_qos_provider() { var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod("get") + .WithUpstreamHttpMethod(new List { "get" }) .WithIsQos(false) .Build(); @@ -51,7 +52,7 @@ namespace Ocelot.UnitTests.Requester .Build(); var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod("get") + .WithUpstreamHttpMethod(new List { "get" }) .WithIsQos(true) .WithQosOptions(qosOptions) .Build();