Merge pull request #29 from TomPallister/develop

update .netcore 2
This commit is contained in:
geffzhang 2017-11-04 11:29:54 +08:00 committed by GitHub
commit c11321bb57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 2511 additions and 2060 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26228.4 VisualStudioVersion = 15.0.26730.15
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject EndProject
@ -12,9 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
build-and-run-tests.ps1 = build-and-run-tests.ps1 build-and-run-tests.ps1 = build-and-run-tests.ps1
build.cake = build.cake build.cake = build.cake
build.ps1 = build.ps1 build.ps1 = build.ps1
build.readme.md = build.readme.md
configuration-explanation.txt = configuration-explanation.txt
configuration.yaml = configuration.yaml
GitVersion.yml = GitVersion.yml GitVersion.yml = GitVersion.yml
global.json = global.json global.json = global.json
LICENSE.md = LICENSE.md LICENSE.md = LICENSE.md
@ -84,4 +81,7 @@ Global
{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B} {106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B} {D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
EndGlobalSection
EndGlobal EndGlobal

View File

@ -33,7 +33,7 @@ That is basically it with a bunch of other features.
## How to install ## How to install
Ocelot is designed to work with ASP.NET core only and is currently Ocelot is designed to work with ASP.NET core only and is currently
built to netcoreapp1.1 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you. built to netcoreapp2.0 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
Install Ocelot and it's dependencies using NuGet. Install Ocelot and it's dependencies using NuGet.

View File

@ -1,6 +1,7 @@
#tool "nuget:?package=GitVersion.CommandLine" #tool "nuget:?package=GitVersion.CommandLine"
#tool "nuget:?package=GitReleaseNotes" #tool "nuget:?package=GitReleaseNotes"
#addin "nuget:?package=Cake.Json" #addin nuget:?package=Cake.Json
#addin nuget:?package=Newtonsoft.Json&version=9.0.1
#tool "nuget:?package=OpenCover" #tool "nuget:?package=OpenCover"
#tool "nuget:?package=ReportGenerator" #tool "nuget:?package=ReportGenerator"
#tool coveralls.net #tool coveralls.net
@ -260,11 +261,19 @@ Task("ReleasePackagesToUnstableFeed")
Task("EnsureStableReleaseRequirements") Task("EnsureStableReleaseRequirements")
.Does(() => .Does(() =>
{ {
Information("Check if stable release...");
if (!AppVeyor.IsRunningOnAppVeyor) if (!AppVeyor.IsRunningOnAppVeyor)
{ {
throw new Exception("Stable release should happen via appveyor"); throw new Exception("Stable release should happen via appveyor");
} }
Information("Running on AppVeyor...");
Information("IsTag = " + AppVeyor.Environment.Repository.Tag.IsTag);
Information("Name = " + AppVeyor.Environment.Repository.Tag.Name);
var isTag = var isTag =
AppVeyor.Environment.Repository.Tag.IsTag && AppVeyor.Environment.Repository.Tag.IsTag &&
!string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name); !string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name);
@ -273,6 +282,8 @@ Task("EnsureStableReleaseRequirements")
{ {
throw new Exception("Stable release should happen from a published GitHub release"); throw new Exception("Stable release should happen from a published GitHub release");
} }
Information("Release is stable...");
}); });
Task("UpdateVersionInfo") Task("UpdateVersionInfo")
@ -287,19 +298,48 @@ Task("DownloadGitHubReleaseArtifacts")
.IsDependentOn("UpdateVersionInfo") .IsDependentOn("UpdateVersionInfo")
.Does(() => .Does(() =>
{ {
try
{
Information("DownloadGitHubReleaseArtifacts");
EnsureDirectoryExists(packagesDir); EnsureDirectoryExists(packagesDir);
Information("Directory exists...");
var releaseUrl = tagsUrl + releaseTag; var releaseUrl = tagsUrl + releaseTag;
var assets_url = ParseJson(GetResource(releaseUrl))
Information("Release url " + releaseUrl);
//var releaseJson = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl));
var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl))
.GetValue("assets_url") .GetValue("assets_url")
.Value<string>(); .Value<string>();
foreach(var asset in DeserializeJson<JArray>(GetResource(assets_url))) Information("Assets url " + assets_url);
var assets = GetResource(assets_url);
Information("Assets " + assets_url);
foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject<JArray>(assets))
{ {
Information("In the loop..");
var file = packagesDir + File(asset.Value<string>("name")); var file = packagesDir + File(asset.Value<string>("name"));
Information("Downloading " + file); Information("Downloading " + file);
DownloadFile(asset.Value<string>("browser_download_url"), file); DownloadFile(asset.Value<string>("browser_download_url"), file);
} }
Information("Out of the loop...");
}
catch(Exception exception)
{
Information("There was an exception " + exception);
throw;
}
}); });
Task("ReleasePackagesToStableFeed") Task("ReleasePackagesToStableFeed")
@ -393,6 +433,8 @@ private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFi
/// gets the resource from the specified url /// gets the resource from the specified url
private string GetResource(string url) private string GetResource(string url)
{
try
{ {
Information("Getting resource from " + url); Information("Getting resource from " + url);
@ -405,7 +447,17 @@ private string GetResource(string url)
{ {
var assetsStream = assetsResponse.GetResponseStream(); var assetsStream = assetsResponse.GetResponseStream();
var assetsReader = new StreamReader(assetsStream); var assetsReader = new StreamReader(assetsStream);
return assetsReader.ReadToEnd(); var response = assetsReader.ReadToEnd();
Information("Response is " + response);
return response;
}
}
catch(Exception exception)
{
Information("There was an exception " + exception);
throw;
} }
} }

117
build.ps1
View File

@ -21,34 +21,35 @@ The build script target to run.
The build configuration to use. The build configuration to use.
.PARAMETER Verbosity .PARAMETER Verbosity
Specifies the amount of information to be displayed. Specifies the amount of information to be displayed.
.PARAMETER ShowDescription
Shows description about tasks.
.PARAMETER DryRun
Performs a dry run.
.PARAMETER Experimental .PARAMETER Experimental
Tells Cake to use the latest Roslyn release. Uses the nightly builds of the Roslyn script engine.
.PARAMETER WhatIf
Performs a dry run of the build script.
No tasks will be executed.
.PARAMETER Mono .PARAMETER Mono
Tells Cake to use the Mono scripting engine. Uses the Mono Compiler rather than the Roslyn script engine.
.PARAMETER SkipToolPackageRestore .PARAMETER SkipToolPackageRestore
Skips restoring of packages. Skips restoring of packages.
.PARAMETER ScriptArgs .PARAMETER ScriptArgs
Remaining arguments are added here. Remaining arguments are added here.
.LINK .LINK
http://cakebuild.net https://cakebuild.net
#> #>
[CmdletBinding()] [CmdletBinding()]
Param( Param(
[string]$Script = "build.cake", [string]$Script = "build.cake",
[string]$Target = "Default", [string]$Target,
[ValidateSet("Release", "Debug")] [string]$Configuration,
[string]$Configuration = "Release",
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity = "Verbose", [string]$Verbosity,
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
[switch]$Experimental, [switch]$Experimental,
[Alias("DryRun","Noop")]
[switch]$WhatIf,
[switch]$Mono, [switch]$Mono,
[switch]$SkipToolPackageRestore, [switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
@ -80,6 +81,15 @@ function MD5HashFile([string] $filePath)
} }
} }
function GetProxyEnabledWebClient
{
$wc = New-Object System.Net.WebClient
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$wc.Proxy = $proxy
return $wc
}
Write-Host "Preparing to run build script..." Write-Host "Preparing to run build script..."
if(!$PSScriptRoot){ if(!$PSScriptRoot){
@ -87,31 +97,15 @@ if(!$PSScriptRoot){
} }
$TOOLS_DIR = Join-Path $PSScriptRoot "tools" $TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
# Should we use mono? $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
$UseMono = "";
if($Mono.IsPresent) {
Write-Verbose -Message "Using the Mono based scripting engine."
$UseMono = "-mono"
}
# Should we use the new Roslyn?
$UseExperimental = "";
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
Write-Verbose -Message "Using experimental version of Roslyn."
$UseExperimental = "-experimental"
}
# Is this a dry run?
$UseDryRun = "";
if($WhatIf.IsPresent) {
$UseDryRun = "-dryrun"
}
# Make sure tools folder exists # Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
@ -122,7 +116,9 @@ if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
# Make sure that packages.config exist. # Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) { if (!(Test-Path $PACKAGES_CONFIG)) {
Write-Verbose -Message "Downloading packages.config..." Write-Verbose -Message "Downloading packages.config..."
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { try {
$wc = GetProxyEnabledWebClient
$wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
Throw "Could not download packages.config." Throw "Could not download packages.config."
} }
} }
@ -130,7 +126,7 @@ if (!(Test-Path $PACKAGES_CONFIG)) {
# Try find NuGet.exe in path if not exists # Try find NuGet.exe in path if not exists
if (!(Test-Path $NUGET_EXE)) { if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Trying to find nuget.exe in PATH..." Write-Verbose -Message "Trying to find nuget.exe in PATH..."
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
@ -142,7 +138,8 @@ if (!(Test-Path $NUGET_EXE)) {
if (!(Test-Path $NUGET_EXE)) { if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..." Write-Verbose -Message "Downloading NuGet.exe..."
try { try {
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) $wc = GetProxyEnabledWebClient
$wc.DownloadFile($NUGET_URL, $NUGET_EXE)
} catch { } catch {
Throw "Could not download NuGet.exe." Throw "Could not download NuGet.exe."
} }
@ -175,6 +172,41 @@ if(-Not $SkipToolPackageRestore.IsPresent) {
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
} }
Write-Verbose -Message ($NuGetOutput | out-string) Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore addins from NuGet
if (Test-Path $ADDINS_PACKAGES_CONFIG) {
Push-Location
Set-Location $ADDINS_DIR
Write-Verbose -Message "Restoring addins from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occured while restoring NuGet addins."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore modules from NuGet
if (Test-Path $MODULES_PACKAGES_CONFIG) {
Push-Location
Set-Location $MODULES_DIR
Write-Verbose -Message "Restoring modules from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occured while restoring NuGet modules."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location Pop-Location
} }
@ -183,7 +215,20 @@ if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE" Throw "Could not find Cake.exe at $CAKE_EXE"
} }
# Build Cake arguments
$cakeArguments = @("$Script");
if ($Target) { $cakeArguments += "-target=$Target" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
if ($ShowDescription) { $cakeArguments += "-showdescription" }
if ($DryRun) { $cakeArguments += "-dryrun" }
if ($Experimental) { $cakeArguments += "-experimental" }
if ($Mono) { $cakeArguments += "-mono" }
$cakeArguments += $ScriptArgs
# Start Cake # Start Cake
Write-Host "Running build script..." Write-Host "Running build script..."
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" &$CAKE_EXE $cakeArguments
exit $LASTEXITCODE exit $LASTEXITCODE

View File

@ -1 +1,6 @@
{"projects":["src","test"]} {
"projects": [ "src", "test" ],
"sdk": {
"version": "2.0.0"
}
}

View File

@ -1,14 +0,0 @@
namespace Ocelot.Authentication.Handler
{
public class AuthenticationHandler
{
public AuthenticationHandler(string provider, IHandler handler)
{
Provider = provider;
Handler = handler;
}
public string Provider { get; private set; }
public IHandler Handler { get; private set; }
}
}

View File

@ -1,53 +0,0 @@
using System;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Creator
{
using Ocelot.Configuration;
using AuthenticationOptions = Configuration.AuthenticationOptions;
/// <summary>
/// Cannot unit test things in this class due to use of extension methods
/// </summary>
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
{
public Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions)
{
var builder = app.New();
if (authOptions.Provider.ToLower() == "jwt")
{
var authenticationConfig = authOptions.Config as JwtConfig;
builder.UseJwtBearerAuthentication(
new JwtBearerOptions()
{
Authority = authenticationConfig.Authority,
Audience = authenticationConfig.Audience
});
}
else
{
var authenticationConfig = authOptions.Config as IdentityServerConfig;
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = authenticationConfig.ProviderRootUrl,
ApiName = authenticationConfig.ApiName,
RequireHttpsMetadata = authenticationConfig.RequireHttps,
AllowedScopes = authOptions.AllowedScopes,
SupportedTokens = SupportedTokens.Both,
ApiSecret = authenticationConfig.ApiSecret
});
}
var authenticationNext = builder.Build();
return new OkResponse<RequestDelegate>(authenticationNext);
}
}
}

View File

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Creator
{
using AuthenticationOptions = Configuration.AuthenticationOptions;
public interface IAuthenticationHandlerCreator
{
Response<RequestDelegate> Create(IApplicationBuilder app, AuthenticationOptions authOptions);
}
}

View File

@ -1,36 +0,0 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Ocelot.Authentication.Handler.Creator;
using Ocelot.Errors;
using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Factory
{
using AuthenticationOptions = Configuration.AuthenticationOptions;
public class AuthenticationHandlerFactory : IAuthenticationHandlerFactory
{
private readonly IAuthenticationHandlerCreator _creator;
public AuthenticationHandlerFactory(IAuthenticationHandlerCreator creator)
{
_creator = creator;
}
public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
{
var handler = _creator.Create(app, authOptions);
if (!handler.IsError)
{
return new OkResponse<AuthenticationHandler>(
new AuthenticationHandler(authOptions.Provider, new RequestDelegateHandler(handler.Data)));
}
return new ErrorResponse<AuthenticationHandler>(new List<Error>
{
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {authOptions.Provider}")
});
}
}
}

View File

@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Factory
{
using AuthenticationOptions = Configuration.AuthenticationOptions;
public interface IAuthenticationHandlerFactory
{
Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions);
}
}

View File

@ -1,12 +0,0 @@
using Ocelot.Errors;
namespace Ocelot.Authentication.Handler.Factory
{
public class UnableToCreateAuthenticationHandlerError : Error
{
public UnableToCreateAuthenticationHandlerError(string message)
: base(message, OcelotErrorCode.UnableToCreateAuthenticationHandlerError)
{
}
}
}

View File

@ -1,10 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Authentication.Handler
{
public interface IHandler
{
Task Handle(HttpContext context);
}
}

View File

@ -1,20 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Ocelot.Authentication.Handler
{
public class RequestDelegateHandler : IHandler
{
private readonly RequestDelegate _requestDelegate;
public RequestDelegateHandler(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
}
public async Task Handle(HttpContext context)
{
await _requestDelegate.Invoke(context);
}
}
}

View File

@ -1,59 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Configuration;
namespace Ocelot.Authentication.JsonConverters
{
public class AuthenticationConfigConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var setting = default(IAuthenticationConfig);
if (jsonObject["Provider"] != null)
{
switch (jsonObject["Provider"].Value<string>())
{
case "Jwt":
setting = new JwtConfig(
jsonObject["Authority"].Value<string>(),
jsonObject["Audience"].Value<string>());
break;
default:
setting = new IdentityServerConfig(
jsonObject["ProviderRootUrl"].Value<string>(),
jsonObject["ApiName"].Value<string>(),
jsonObject["RequireHttps"].Value<bool>(),
jsonObject["ApiSecret"].Value<string>());
break;
}
}
else
{
setting = new IdentityServerConfig(string.Empty, string.Empty, false, string.Empty);
}
serializer.Populate(jsonObject.CreateReader(), setting);
return setting;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IAuthenticationConfig);
}
}
}

View File

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Infrastructure.Extensions; using Ocelot.Infrastructure.Extensions;
@ -16,18 +16,16 @@ namespace Ocelot.Authentication.Middleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IApplicationBuilder _app; private readonly IApplicationBuilder _app;
private readonly IAuthenticationHandlerFactory _authHandlerFactory; private readonly IAuthenticationSchemeProvider _authSchemeProvider;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
public AuthenticationMiddleware(RequestDelegate next, public AuthenticationMiddleware(RequestDelegate next,
IApplicationBuilder app, IApplicationBuilder app,
IRequestScopedDataRepository requestScopedDataRepository, IRequestScopedDataRepository requestScopedDataRepository,
IAuthenticationHandlerFactory authHandlerFactory,
IOcelotLoggerFactory loggerFactory) IOcelotLoggerFactory loggerFactory)
: base(requestScopedDataRepository) : base(requestScopedDataRepository)
{ {
_next = next; _next = next;
_authHandlerFactory = authHandlerFactory;
_app = app; _app = app;
_logger = loggerFactory.CreateLogger<AuthenticationMiddleware>(); _logger = loggerFactory.CreateLogger<AuthenticationMiddleware>();
} }
@ -38,17 +36,9 @@ namespace Ocelot.Authentication.Middleware
{ {
_logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
var authenticationHandler = _authHandlerFactory.Get(_app, DownstreamRoute.ReRoute.AuthenticationOptions); var result = await context.AuthenticateAsync(DownstreamRoute.ReRoute.AuthenticationOptions.AuthenticationProviderKey);
if (authenticationHandler.IsError)
{
_logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}");
SetPipelineError(authenticationHandler.Errors);
return;
}
await authenticationHandler.Data.Handler.Handle(context);
context.User = result.Principal;
if (context.User.Identity.IsAuthenticated) if (context.User.Identity.IsAuthenticated)
{ {
@ -65,7 +55,6 @@ namespace Ocelot.Authentication.Middleware
_logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}"); _logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}");
SetPipelineError(error); SetPipelineError(error);
return;
} }
} }
else else

View File

@ -2,52 +2,15 @@
namespace Ocelot.Configuration namespace Ocelot.Configuration
{ {
using Newtonsoft.Json;
public class AuthenticationOptions public class AuthenticationOptions
{ {
public AuthenticationOptions(string provider, List<string> allowedScopes, IAuthenticationConfig config) public AuthenticationOptions(List<string> allowedScopes, string authenticationProviderKey)
{ {
Provider = provider;
AllowedScopes = allowedScopes; AllowedScopes = allowedScopes;
Config = config; AuthenticationProviderKey = authenticationProviderKey;
} }
public string Provider { get; private set; }
public List<string> AllowedScopes { get; private set; } public List<string> AllowedScopes { get; private set; }
public string AuthenticationProviderKey { get; private set; }
public IAuthenticationConfig Config { get; private set; }
} }
public class IdentityServerConfig : IAuthenticationConfig
{
public IdentityServerConfig(string providerRootUrl, string apiName, bool requireHttps, string apiSecret)
{
ProviderRootUrl = providerRootUrl;
ApiName = apiName;
RequireHttps = requireHttps;
ApiSecret = apiSecret;
}
public string ProviderRootUrl { get; private set; }
public string ApiName { get; private set; }
public string ApiSecret { get; private set; }
public bool RequireHttps { get; private set; }
}
public class JwtConfig : IAuthenticationConfig
{
public JwtConfig(string authority, string audience)
{
Audience = audience;
Authority = authority;
}
public string Audience { get; }
public string Authority { get; }
}
public interface IAuthenticationConfig {}
} }

View File

@ -4,18 +4,8 @@ namespace Ocelot.Configuration.Builder
{ {
public class AuthenticationOptionsBuilder public class AuthenticationOptionsBuilder
{ {
private List<string> _allowedScopes = new List<string>();
private string _provider; private string _authenticationProviderKey;
private List<string> _allowedScopes;
private IAuthenticationConfig _identityServerConfig;
public AuthenticationOptionsBuilder WithProvider(string provider)
{
_provider = provider;
return this;
}
public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes) public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes)
{ {
@ -23,76 +13,15 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config) public AuthenticationOptionsBuilder WithAuthenticationProviderKey(string authenticationProviderKey)
{ {
_identityServerConfig = config; _authenticationProviderKey = authenticationProviderKey;
return this; return this;
} }
public AuthenticationOptions Build() public AuthenticationOptions Build()
{ {
return new AuthenticationOptions(_provider, _allowedScopes, _identityServerConfig); return new AuthenticationOptions(_allowedScopes, _authenticationProviderKey);
}
}
public class IdentityServerConfigBuilder
{
private string _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
public IdentityServerConfigBuilder WithProviderRootUrl(string providerRootUrl)
{
_providerRootUrl = providerRootUrl;
return this;
}
public IdentityServerConfigBuilder WithApiName(string apiName)
{
_apiName = apiName;
return this;
}
public IdentityServerConfigBuilder WithApiSecret(string apiSecret)
{
_apiSecret = apiSecret;
return this;
}
public IdentityServerConfigBuilder WithRequireHttps(bool requireHttps)
{
_requireHttps = requireHttps;
return this;
}
public IdentityServerConfig Build()
{
return new IdentityServerConfig(_providerRootUrl, _apiName, _requireHttps, _apiSecret);
}
}
public class JwtConfigBuilder
{
public string _authority;
public string _audience;
public JwtConfigBuilder WithAuthority(string authority)
{
_authority = authority;
return this;
}
public JwtConfigBuilder WithAudience(string audience)
{
_audience = audience;
return this;
}
public JwtConfig Build()
{
return new JwtConfig(_authority, _audience);
} }
} }
} }

View File

@ -29,8 +29,10 @@ namespace Ocelot.Configuration.Builder
private ServiceProviderConfiguration _serviceProviderConfiguraion; private ServiceProviderConfiguration _serviceProviderConfiguraion;
private bool _useQos; private bool _useQos;
private QoSOptions _qosOptions; private QoSOptions _qosOptions;
private HttpHandlerOptions _httpHandlerOptions;
public bool _enableRateLimiting; public bool _enableRateLimiting;
public RateLimitOptions _rateLimitOptions; public RateLimitOptions _rateLimitOptions;
private string _authenticationProviderKey;
public ReRouteBuilder WithLoadBalancer(string loadBalancer) public ReRouteBuilder WithLoadBalancer(string loadBalancer)
{ {
@ -176,6 +178,17 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ReRouteBuilder WithAuthenticationProviderKey(string authenticationProviderKey)
{
_authenticationProviderKey = authenticationProviderKey;
return this;
}
public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input)
{
_httpHandlerOptions = input;
return this;
}
public ReRoute Build() public ReRoute Build()
{ {
@ -203,7 +216,8 @@ namespace Ocelot.Configuration.Builder
_useQos, _useQos,
_qosOptions, _qosOptions,
_enableRateLimiting, _enableRateLimiting,
_rateLimitOptions); _rateLimitOptions,
_httpHandlerOptions);
} }
} }
} }

View File

@ -1,27 +1,12 @@
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator namespace Ocelot.Configuration.Creator
{ {
public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
{ {
private readonly IAuthenticationProviderConfigCreator _creator; public AuthenticationOptions Create(FileReRoute reRoute)
public AuthenticationOptionsCreator(IAuthenticationProviderConfigCreator creator)
{ {
_creator = creator; return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes, reRoute.AuthenticationOptions.AuthenticationProviderKey);
}
public AuthenticationOptions Create(FileReRoute fileReRoute)
{
var authenticationConfig = _creator.Create(fileReRoute.AuthenticationOptions);
return new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig)
.Build();
} }
} }
} }

View File

@ -1,37 +0,0 @@
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator
{
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
public class AuthenticationProviderConfigCreator : IAuthenticationProviderConfigCreator
{
public IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions)
{
if (authenticationOptions.Provider?.ToLower() == "jwt")
{
return CreateJwt(authenticationOptions);
}
return CreateIdentityServer(authenticationOptions);
}
private JwtConfig CreateJwt(FileAuthenticationOptions authenticationOptions)
{
return new JwtConfigBuilder()
.WithAudience(authenticationOptions.JwtConfig?.Audience)
.WithAuthority(authenticationOptions.JwtConfig?.Authority)
.Build();
}
private IdentityServerConfig CreateIdentityServer(FileAuthenticationOptions authenticationOptions)
{
return new IdentityServerConfigBuilder()
.WithApiName(authenticationOptions.IdentityServerConfig?.ApiName)
.WithApiSecret(authenticationOptions.IdentityServerConfig?.ApiSecret)
.WithProviderRootUrl(authenticationOptions.IdentityServerConfig?.ProviderRootUrl)
.WithRequireHttps(authenticationOptions.IdentityServerConfig.RequireHttps).Build();
}
}
}

View File

@ -38,6 +38,7 @@ namespace Ocelot.Configuration.Creator
private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator; private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
private readonly IRegionCreator _regionCreator; private readonly IRegionCreator _regionCreator;
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
public FileOcelotConfigurationCreator( public FileOcelotConfigurationCreator(
IOptions<FileConfiguration> options, IOptions<FileConfiguration> options,
@ -55,7 +56,8 @@ namespace Ocelot.Configuration.Creator
IQoSOptionsCreator qosOptionsCreator, IQoSOptionsCreator qosOptionsCreator,
IReRouteOptionsCreator fileReRouteOptionsCreator, IReRouteOptionsCreator fileReRouteOptionsCreator,
IRateLimitOptionsCreator rateLimitOptionsCreator, IRateLimitOptionsCreator rateLimitOptionsCreator,
IRegionCreator regionCreator IRegionCreator regionCreator,
IHttpHandlerOptionsCreator httpHandlerOptionsCreator
) )
{ {
_regionCreator = regionCreator; _regionCreator = regionCreator;
@ -74,6 +76,7 @@ namespace Ocelot.Configuration.Creator
_serviceProviderConfigCreator = serviceProviderConfigCreator; _serviceProviderConfigCreator = serviceProviderConfigCreator;
_qosOptionsCreator = qosOptionsCreator; _qosOptionsCreator = qosOptionsCreator;
_fileReRouteOptionsCreator = fileReRouteOptionsCreator; _fileReRouteOptionsCreator = fileReRouteOptionsCreator;
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
} }
public async Task<Response<IOcelotConfiguration>> Create() public async Task<Response<IOcelotConfiguration>> Create()
@ -92,7 +95,7 @@ namespace Ocelot.Configuration.Creator
private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration) private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
{ {
var response = _configurationValidator.IsValid(fileConfiguration); var response = await _configurationValidator.IsValid(fileConfiguration);
if (response.Data.IsError) if (response.Data.IsError)
{ {
@ -143,6 +146,8 @@ namespace Ocelot.Configuration.Creator
var region = _regionCreator.Create(fileReRoute); var region = _regionCreator.Create(fileReRoute);
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute);
var reRoute = new ReRouteBuilder() var reRoute = new ReRouteBuilder()
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
@ -168,6 +173,7 @@ namespace Ocelot.Configuration.Creator
.WithQosOptions(qosOptions) .WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption) .WithRateLimitOptions(rateLimitOption)
.WithHttpHandlerOptions(httpHandlerOptions)
.Build(); .Build();
await SetupLoadBalancer(reRoute); await SetupLoadBalancer(reRoute);
@ -178,7 +184,7 @@ namespace Ocelot.Configuration.Creator
private string CreateReRouteKey(FileReRoute fileReRoute) 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 //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}"; var loadBalancerKey = $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}";
return loadBalancerKey; return loadBalancerKey;
} }

View File

@ -0,0 +1,13 @@
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator
{
public HttpHandlerOptions Create(FileReRoute fileReRoute)
{
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
fileReRoute.HttpHandlerOptions.UseCookieContainer);
}
}
}

View File

@ -1,9 +1,10 @@
using System.Collections.Generic;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator namespace Ocelot.Configuration.Creator
{ {
public interface IAuthenticationOptionsCreator public interface IAuthenticationOptionsCreator
{ {
AuthenticationOptions Create(FileReRoute fileReRoute); AuthenticationOptions Create(FileReRoute reRoute);
} }
} }

View File

@ -0,0 +1,12 @@
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Creator
{
/// <summary>
/// Describes creation of HttpHandlerOptions
/// </summary>
public interface IHttpHandlerOptionsCreator
{
HttpHandlerOptions Create(FileReRoute fileReRoute);
}
}

View File

@ -36,7 +36,7 @@ namespace Ocelot.Configuration.Creator
private bool IsAuthenticated(FileReRoute fileReRoute) private bool IsAuthenticated(FileReRoute fileReRoute)
{ {
return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.Provider); return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey);
} }
private bool IsAuthorised(FileReRoute fileReRoute) private bool IsAuthorised(FileReRoute fileReRoute)

View File

@ -41,8 +41,8 @@ namespace Ocelot.Configuration.Creator
} }
var route = reRoute.ReRouteIsCaseSensitive var route = reRoute.ReRouteIsCaseSensitive
? $"{upstreamTemplate}{RegExMatchEndString}" ? $"^{upstreamTemplate}{RegExMatchEndString}"
: $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; : $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}";
return route; return route;
} }

View File

@ -7,13 +7,9 @@ namespace Ocelot.Configuration.File
public FileAuthenticationOptions() public FileAuthenticationOptions()
{ {
AllowedScopes = new List<string>(); AllowedScopes = new List<string>();
IdentityServerConfig = new FileIdentityServerConfig();
JwtConfig = new FileJwtConfig();
} }
public string Provider { get; set; } public string AuthenticationProviderKey {get; set;}
public List<string> AllowedScopes { get; set; } public List<string> AllowedScopes { get; set; }
public FileIdentityServerConfig IdentityServerConfig { get; set; }
public FileJwtConfig JwtConfig { get; set; }
} }
} }

View File

@ -0,0 +1,15 @@
namespace Ocelot.Configuration.File
{
public class FileHttpHandlerOptions
{
public FileHttpHandlerOptions()
{
AllowAutoRedirect = true;
UseCookieContainer = true;
}
public bool AllowAutoRedirect { get; set; }
public bool UseCookieContainer { get; set; }
}
}

View File

@ -11,16 +11,16 @@ namespace Ocelot.Configuration.File
AddClaimsToRequest = new Dictionary<string, string>(); AddClaimsToRequest = new Dictionary<string, string>();
RouteClaimsRequirement = new Dictionary<string, string>(); RouteClaimsRequirement = new Dictionary<string, string>();
AddQueriesToRequest = new Dictionary<string, string>(); AddQueriesToRequest = new Dictionary<string, string>();
AuthenticationOptions = new FileAuthenticationOptions();
FileCacheOptions = new FileCacheOptions(); FileCacheOptions = new FileCacheOptions();
QoSOptions = new FileQoSOptions(); QoSOptions = new FileQoSOptions();
RateLimitOptions = new FileRateLimitRule(); RateLimitOptions = new FileRateLimitRule();
AuthenticationOptions = new FileAuthenticationOptions();
HttpHandlerOptions = new FileHttpHandlerOptions();
} }
public string DownstreamPathTemplate { get; set; } public string DownstreamPathTemplate { get; set; }
public string UpstreamPathTemplate { get; set; } public string UpstreamPathTemplate { get; set; }
public List<string> UpstreamHttpMethod { get; set; } public List<string> UpstreamHttpMethod { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; }
public Dictionary<string, string> AddHeadersToRequest { get; set; } public Dictionary<string, string> AddHeadersToRequest { get; set; }
public Dictionary<string, string> AddClaimsToRequest { get; set; } public Dictionary<string, string> AddClaimsToRequest { get; set; }
public Dictionary<string, string> RouteClaimsRequirement { get; set; } public Dictionary<string, string> RouteClaimsRequirement { get; set; }
@ -35,5 +35,7 @@ namespace Ocelot.Configuration.File
public FileQoSOptions QoSOptions { get; set; } public FileQoSOptions QoSOptions { get; set; }
public string LoadBalancer {get;set;} public string LoadBalancer {get;set;}
public FileRateLimitRule RateLimitOptions { get; set; } public FileRateLimitRule RateLimitOptions { get; set; }
public FileAuthenticationOptions AuthenticationOptions { get; set; }
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
} }
} }

View File

@ -0,0 +1,25 @@
namespace Ocelot.Configuration
{
/// <summary>
/// Describes configuration parameters for http handler,
/// that is created to handle a request to service
/// </summary>
public class HttpHandlerOptions
{
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer)
{
AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer;
}
/// <summary>
/// Specify if auto redirect is enabled
/// </summary>
public bool AllowAutoRedirect { get; private set; }
/// <summary>
/// Specify is handler has to use a cookie container
/// </summary>
public bool UseCookieContainer { get; private set; }
}
}

View File

@ -29,7 +29,8 @@ namespace Ocelot.Configuration
bool isQos, bool isQos,
QoSOptions qosOptions, QoSOptions qosOptions,
bool enableEndpointRateLimiting, bool enableEndpointRateLimiting,
RateLimitOptions ratelimitOptions) RateLimitOptions ratelimitOptions,
HttpHandlerOptions httpHandlerOptions)
{ {
ReRouteKey = reRouteKey; ReRouteKey = reRouteKey;
ServiceProviderConfiguraion = serviceProviderConfiguraion; ServiceProviderConfiguraion = serviceProviderConfiguraion;
@ -58,6 +59,7 @@ namespace Ocelot.Configuration
QosOptionsOptions = qosOptions; QosOptionsOptions = qosOptions;
EnableEndpointEndpointRateLimiting = enableEndpointRateLimiting; EnableEndpointEndpointRateLimiting = enableEndpointRateLimiting;
RateLimitOptions = ratelimitOptions; RateLimitOptions = ratelimitOptions;
HttpHandlerOptions = httpHandlerOptions;
} }
public string ReRouteKey {get;private set;} public string ReRouteKey {get;private set;}
@ -84,5 +86,6 @@ namespace Ocelot.Configuration
public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; } public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; }
public bool EnableEndpointEndpointRateLimiting { get; private set; } public bool EnableEndpointEndpointRateLimiting { get; private set; }
public RateLimitOptions RateLimitOptions { get; private set; } public RateLimitOptions RateLimitOptions { get; private set; }
public HttpHandlerOptions HttpHandlerOptions { get; private set; }
} }
} }

View File

@ -3,8 +3,6 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Consul; using Consul;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
@ -49,9 +47,7 @@ namespace Ocelot.Configuration.Repository
var json = Encoding.UTF8.GetString(bytes); var json = Encoding.UTF8.GetString(bytes);
var settings = new JsonSerializerSettings(); var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
settings.Converters.Add(new AuthenticationConfigConverter());
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
return new OkResponse<IOcelotConfiguration>(consulConfig); return new OkResponse<IOcelotConfiguration>(consulConfig);
} }

View File

@ -0,0 +1,12 @@
using Ocelot.Errors;
namespace Ocelot.Configuration.Validator
{
public class PathTemplateDoesntStartWithForwardSlash : Error
{
public PathTemplateDoesntStartWithForwardSlash(string message)
: base(message, OcelotErrorCode.PathTemplateDoesntStartWithForwardSlash)
{
}
}
}

View File

@ -1,7 +1,7 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ocelot.Authentication.Handler; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Responses; using Ocelot.Responses;
@ -10,7 +10,14 @@ namespace Ocelot.Configuration.Validator
{ {
public class FileConfigurationValidator : IConfigurationValidator public class FileConfigurationValidator : IConfigurationValidator
{ {
public Response<ConfigurationValidationResult> IsValid(FileConfiguration configuration) private readonly IAuthenticationSchemeProvider _provider;
public FileConfigurationValidator(IAuthenticationSchemeProvider provider)
{
_provider = provider;
}
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
{ {
var result = CheckForDuplicateReRoutes(configuration); var result = CheckForDuplicateReRoutes(configuration);
@ -19,7 +26,21 @@ namespace Ocelot.Configuration.Validator
return new OkResponse<ConfigurationValidationResult>(result); return new OkResponse<ConfigurationValidationResult>(result);
} }
result = CheckForUnsupportedAuthenticationProviders(configuration); result = CheckDownstreamTemplatePathBeingsWithForwardSlash(configuration);
if (result.IsError)
{
return new OkResponse<ConfigurationValidationResult>(result);
}
result = CheckUpstreamTemplatePathBeingsWithForwardSlash(configuration);
if (result.IsError)
{
return new OkResponse<ConfigurationValidationResult>(result);
}
result = await CheckForUnsupportedAuthenticationProviders(configuration);
if (result.IsError) if (result.IsError)
{ {
@ -42,25 +63,67 @@ namespace Ocelot.Configuration.Validator
return new OkResponse<ConfigurationValidationResult>(result); return new OkResponse<ConfigurationValidationResult>(result);
} }
private ConfigurationValidationResult CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration) private ConfigurationValidationResult CheckDownstreamTemplatePathBeingsWithForwardSlash(FileConfiguration configuration)
{ {
var errors = new List<Error>(); var errors = new List<Error>();
foreach(var reRoute in configuration.ReRoutes) foreach(var reRoute in configuration.ReRoutes)
{ {
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider); if(!reRoute.DownstreamPathTemplate.StartsWith("/"))
{
errors.Add(new PathTemplateDoesntStartWithForwardSlash($"{reRoute.DownstreamPathTemplate} doesnt start with forward slash"));
}
}
if(errors.Any())
{
return new ConfigurationValidationResult(true, errors);
}
return new ConfigurationValidationResult(false, errors);
}
private ConfigurationValidationResult CheckUpstreamTemplatePathBeingsWithForwardSlash(FileConfiguration configuration)
{
var errors = new List<Error>();
foreach(var reRoute in configuration.ReRoutes)
{
if(!reRoute.UpstreamPathTemplate.StartsWith("/"))
{
errors.Add(new PathTemplateDoesntStartWithForwardSlash($"{reRoute.DownstreamPathTemplate} doesnt start with forward slash"));
}
}
if(errors.Any())
{
return new ConfigurationValidationResult(true, errors);
}
return new ConfigurationValidationResult(false, errors);
}
private async Task<ConfigurationValidationResult> CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration)
{
var errors = new List<Error>();
foreach (var reRoute in configuration.ReRoutes)
{
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions.AuthenticationProviderKey);
if (!isAuthenticated) if (!isAuthenticated)
{ {
continue; continue;
} }
if (IsSupportedAuthenticationProvider(reRoute.AuthenticationOptions?.Provider)) var data = await _provider.GetAllSchemesAsync();
var schemes = data.ToList();
if (schemes.Any(x => x.Name == reRoute.AuthenticationOptions.AuthenticationProviderKey))
{ {
continue; continue;
} }
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}"); var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions.AuthenticationProviderKey} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
errors.Add(error); errors.Add(error);
} }
@ -69,13 +132,6 @@ namespace Ocelot.Configuration.Validator
: new ConfigurationValidationResult(false); : new ConfigurationValidationResult(false);
} }
private bool IsSupportedAuthenticationProvider(string provider)
{
SupportedAuthenticationProviders supportedProvider;
return Enum.TryParse(provider, true, out supportedProvider);
}
private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration) private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration)
{ {
var errors = new List<Error>(); var errors = new List<Error>();

View File

@ -1,10 +1,11 @@
using Ocelot.Configuration.File; using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Ocelot.Responses; using Ocelot.Responses;
namespace Ocelot.Configuration.Validator namespace Ocelot.Configuration.Validator
{ {
public interface IConfigurationValidator public interface IConfigurationValidator
{ {
Response<ConfigurationValidationResult> IsValid(FileConfiguration configuration); Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration);
} }
} }

View File

@ -1,10 +0,0 @@
using Ocelot.Configuration;
using Ocelot.Configuration.File;
namespace Ocelot.Creator.Configuration
{
public interface IAuthenticationProviderConfigCreator
{
IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions);
}
}

View File

@ -4,8 +4,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.Authentication.Handler.Creator;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Authorisation; using Ocelot.Authorisation;
using Ocelot.Cache; using Ocelot.Cache;
using Ocelot.Claims; using Ocelot.Claims;
@ -38,13 +36,15 @@ using Ocelot.Responder;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens; using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Creator.Configuration;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider; using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
namespace Ocelot.DependencyInjection namespace Ocelot.DependencyInjection
@ -72,10 +72,8 @@ namespace Ocelot.DependencyInjection
services.Configure<FileConfiguration>(configurationRoot); services.Configure<FileConfiguration>(configurationRoot);
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>(); services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
services.TryAddSingleton<IAuthenticationProviderConfigCreator, AuthenticationProviderConfigCreator>();
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>(); services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>(); services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>(); services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>(); services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>(); services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
@ -84,59 +82,7 @@ namespace Ocelot.DependencyInjection
services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>(); services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>(); services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>(); services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
if(identityServerConfiguration != null)
{
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = services
.AddIdentityServer(options => {
options.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(new List<ApiResource>
{
new ApiResource
{
Name = identityServerConfiguration.ApiName,
Description = identityServerConfiguration.Description,
Enabled = identityServerConfiguration.Enabled,
DisplayName = identityServerConfiguration.ApiName,
Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(),
ApiSecrets = new List<Secret>
{
new Secret
{
Value = identityServerConfiguration.ApiSecret.Sha256()
}
}
}
})
.AddInMemoryClients(new List<Client>
{
new Client
{
ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
AllowedScopes = identityServerConfiguration.AllowedScopes,
AccessTokenType = identityServerConfiguration.AccessTokenType,
Enabled = identityServerConfiguration.Enabled,
RequireClientSecret = identityServerConfiguration.RequireClientSecret
}
}).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
{
identityServerBuilder.AddTemporarySigningCredential();
}
else
{
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
}
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly; var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
@ -175,11 +121,10 @@ namespace Ocelot.DependencyInjection
services.TryAddSingleton<IHttpResponder, HttpContextResponder>(); services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
services.TryAddSingleton<IRequestCreator, HttpRequestCreator>(); services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>(); services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
services.TryAddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
services.TryAddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>(); services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>(); services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
services.TryAddSingleton<IRequestMapper, RequestMapper>(); services.TryAddSingleton<IRequestMapper, RequestMapper>();
services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository // could maybe use a scoped data repository
@ -190,8 +135,89 @@ namespace Ocelot.DependencyInjection
//Used to log the the start and ending of middleware //Used to log the the start and ending of middleware
services.TryAddSingleton<OcelotDiagnosticListener>(); services.TryAddSingleton<OcelotDiagnosticListener>();
services.AddMiddlewareAnalysis(); services.AddMiddlewareAnalysis();
services.AddWebEncoders();
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
if (identityServerConfiguration != null)
{
services.AddIdentityServer(identityServerConfiguration, configurationRoot);
}
return services; return services;
} }
private static void AddIdentityServer(this IServiceCollection services, IIdentityServerConfiguration identityServerConfiguration, IConfigurationRoot configurationRoot)
{
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = services
.AddIdentityServer(o => {
o.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration))
.AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
//todo - refactor a method so we know why this is happening
var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(o =>
{
var adminPath = configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
o.Authority = baseSchemeUrlAndPort + adminPath;
o.ApiName = identityServerConfiguration.ApiName;
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = identityServerConfiguration.ApiSecret;
});
//todo - refactor naming..
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
{
identityServerBuilder.AddDeveloperSigningCredential();
}
else
{
//todo - refactor so calls method?
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
}
private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<ApiResource>
{
new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
{
ApiSecrets = new List<Secret>
{
new Secret
{
Value = identityServerConfiguration.ApiSecret.Sha256()
}
}
}
};
}
private static List<Client> Client(IIdentityServerConfiguration identityServerConfiguration)
{
return new List<Client>
{
new Client
{
ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
AllowedScopes = { identityServerConfiguration.ApiName }
}
};
}
} }
} }

View File

@ -6,6 +6,7 @@ using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Utilities;
namespace Ocelot.DownstreamRouteFinder.Finder namespace Ocelot.DownstreamRouteFinder.Finder
{ {
@ -24,6 +25,8 @@ namespace Ocelot.DownstreamRouteFinder.Finder
public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod) public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
{ {
upstreamUrlPath = upstreamUrlPath.SetLastCharacterAs('/');
var configuration = await _configProvider.Get(); var configuration = await _configProvider.Get();
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower())); var applicableReRoutes = configuration.Data.ReRoutes.Where(r => r.UpstreamHttpMethod.Count == 0 || r.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(upstreamHttpMethod.ToLower()));

View File

@ -30,7 +30,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/'); var upstreamUrlPath = context.Request.Path.ToString();
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath); _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);

View File

@ -49,12 +49,8 @@ namespace Ocelot.Errors.Middleware
} }
private void SetInternalServerErrorOnResponse(HttpContext context) private void SetInternalServerErrorOnResponse(HttpContext context)
{
context.Response.OnStarting(x =>
{ {
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
return Task.CompletedTask;
}, context);
} }
private string CreateMessage(HttpContext context, Exception e) private string CreateMessage(HttpContext context, Exception e)

View File

@ -31,6 +31,7 @@
UnableToFindQoSProviderError, UnableToFindQoSProviderError,
UnableToSetConfigInConsulError, UnableToSetConfigInConsulError,
UnmappableRequestError, UnmappableRequestError,
RateLimitOptionsError RateLimitOptionsError,
PathTemplateDoesntStartWithForwardSlash
} }
} }

View File

@ -1,10 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Authentication.Middleware; using Ocelot.Authentication.Middleware;
using Ocelot.Cache.Middleware; using Ocelot.Cache.Middleware;
using Ocelot.Claims.Middleware; using Ocelot.Claims.Middleware;
using Ocelot.Controllers;
using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware; using Ocelot.Errors.Middleware;
@ -178,25 +181,10 @@ namespace Ocelot.Middleware
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null) if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)
{ {
var urlFinder = (IBaseUrlFinder)builder.ApplicationServices.GetService(typeof(IBaseUrlFinder));
var baseSchemeUrlAndPort = urlFinder.Find();
builder.Map(configuration.AdministrationPath, app => builder.Map(configuration.AdministrationPath, app =>
{ {
var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}";
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityServerUrl,
ApiName = identityServerConfiguration.ApiName,
RequireHttpsMetadata = identityServerConfiguration.RequireHttps,
AllowedScopes = identityServerConfiguration.AllowedScopes,
SupportedTokens = SupportedTokens.Both,
ApiSecret = identityServerConfiguration.ApiSecret
});
app.UseIdentityServer(); app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(); app.UseMvc();
}); });
} }

View File

@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion>2.0.0</NETStandardImplicitPackageVersion>
<Description>This project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.</Description> <Description>This project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.</Description>
<AssemblyTitle>Ocelot</AssemblyTitle> <AssemblyTitle>Ocelot</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework>
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
<AssemblyName>Ocelot</AssemblyName> <AssemblyName>Ocelot</AssemblyName>
<PackageId>Ocelot</PackageId> <PackageId>Ocelot</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags> <PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl> <PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl> <PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
@ -26,37 +26,23 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="1.1.0" /> <PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" /> <PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OAuth" Version="1.1.1" /> <PackageReference Include="CacheManager.Core" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" /> <PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.1" /> <PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1" /> <PackageReference Include="Consul" Version="0.7.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="1.1.1" /> <PackageReference Include="Polly" Version="5.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="1.1.1" /> <PackageReference Include="IdentityServer4" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Twitter" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="1.1.1" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="CacheManager.Core" Version="0.9.2" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="0.9.2" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="0.9.2" />
<PackageReference Include="Consul" Version="0.7.2.1" />
<PackageReference Include="Polly" Version="5.0.3" />
<PackageReference Include="IdentityServer4" Version="1.5.1" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -10,9 +10,11 @@ namespace Ocelot.Request.Builder
public async Task<Response<Request>> Build( public async Task<Response<Request>> Build(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider) IQoSProvider qosProvider,
bool useCookieContainer,
bool allowAutoRedirect)
{ {
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider)); return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, useCookieContainer, allowAutoRedirect));
} }
} }
} }

View File

@ -11,6 +11,8 @@
Task<Response<Request>> Build( Task<Response<Request>> Build(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider); IQoSProvider qosProvider,
bool useCookieContainer,
bool allowAutoRedirect);
} }
} }

View File

@ -46,7 +46,9 @@ namespace Ocelot.Request.Middleware
var buildResult = await _requestCreator.Build( var buildResult = await _requestCreator.Build(
DownstreamRequest, DownstreamRequest,
DownstreamRoute.ReRoute.IsQos, DownstreamRoute.ReRoute.IsQos,
qosProvider.Data); qosProvider.Data,
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect);
if (buildResult.IsError) if (buildResult.IsError)
{ {

View File

@ -8,15 +8,21 @@ namespace Ocelot.Request
public Request( public Request(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider) IQoSProvider qosProvider,
bool allowAutoRedirect,
bool useCookieContainer)
{ {
HttpRequestMessage = httpRequestMessage; HttpRequestMessage = httpRequestMessage;
IsQos = isQos; IsQos = isQos;
QosProvider = qosProvider; QosProvider = qosProvider;
AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer;
} }
public HttpRequestMessage HttpRequestMessage { get; private set; } public HttpRequestMessage HttpRequestMessage { get; private set; }
public bool IsQos { get; private set; } public bool IsQos { get; private set; }
public IQoSProvider QosProvider { get; private set; } public IQoSProvider QosProvider { get; private set; }
public bool AllowAutoRedirect { get; private set; }
public bool UseCookieContainer { get; private set; }
} }
} }

View File

@ -20,9 +20,9 @@ namespace Ocelot.Requester
return this; return this;
} }
public IHttpClient Create() public IHttpClient Create(bool useCookies, bool allowAutoRedirect)
{ {
var httpclientHandler = new HttpClientHandler(); var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies};
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler)); var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));

View File

@ -2,6 +2,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using Polly.CircuitBreaker; using Polly.CircuitBreaker;
@ -14,7 +15,8 @@ namespace Ocelot.Requester
private readonly IHttpClientCache _cacheHandlers; private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers) public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
IHttpClientCache cacheHandlers)
{ {
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>(); _logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
_cacheHandlers = cacheHandlers; _cacheHandlers = cacheHandlers;
@ -26,7 +28,7 @@ namespace Ocelot.Requester
var cacheKey = GetCacheKey(request, builder); var cacheKey = GetCacheKey(request, builder);
var httpClient = GetHttpClient(cacheKey, builder); var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect);
try try
{ {
@ -54,13 +56,13 @@ namespace Ocelot.Requester
} }
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder) private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect)
{ {
var httpClient = _cacheHandlers.Get(cacheKey); var httpClient = _cacheHandlers.Get(cacheKey);
if (httpClient == null) if (httpClient == null)
{ {
httpClient = builder.Create(); httpClient = builder.Create(useCookieContainer, allowAutoRedirect);
} }
return httpClient; return httpClient;
} }

View File

@ -20,6 +20,8 @@ namespace Ocelot.Requester
/// <summary> /// <summary>
/// Creates the <see cref="HttpClient"/> /// Creates the <see cref="HttpClient"/>
/// </summary> /// </summary>
IHttpClient Create(); /// <param name="useCookies">Defines if http client should use cookie container</param>
/// <param name="allowAutoRedirect">Defines if http client should allow auto redirect</param>
IHttpClient Create(bool useCookies, bool allowAutoRedirect);
} }
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -14,7 +15,6 @@ using Xunit;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using IdentityServer4;
using IdentityServer4.Test; using IdentityServer4.Test;
public class AuthenticationTests : IDisposable public class AuthenticationTests : IDisposable
@ -28,10 +28,19 @@ namespace Ocelot.AcceptanceTests
private int _downstreamServicePort = 51876; private int _downstreamServicePort = 51876;
private string _downstreamServiceScheme = "http"; private string _downstreamServiceScheme = "http";
private string _downstreamServiceUrl = "http://localhost:51876"; private string _downstreamServiceUrl = "http://localhost:51876";
private readonly Action<IdentityServerAuthenticationOptions> _options;
public AuthenticationTests() public AuthenticationTests()
{ {
_steps = new Steps(); _steps = new Steps();
_options = o =>
{
o.Authority = _identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
} }
[Fact] [Fact]
@ -51,14 +60,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
} }
} }
} }
@ -67,7 +69,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
@ -91,14 +93,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
} }
} }
} }
@ -108,7 +103,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
@ -133,14 +128,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
} }
} }
} }
@ -150,7 +138,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl)) .And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
@ -172,17 +160,9 @@ namespace Ocelot.AcceptanceTests
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
} }
} }
} }
@ -192,7 +172,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
@ -217,14 +197,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
} }
} }
} }
@ -234,7 +207,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
@ -275,7 +248,7 @@ namespace Ocelot.AcceptanceTests
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddTemporarySigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -22,10 +23,20 @@ namespace Ocelot.AcceptanceTests
private IWebHost _servicebuilder; private IWebHost _servicebuilder;
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private readonly Steps _steps; private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl = "http://localhost:51888";
public AuthorisationTests() public AuthorisationTests()
{ {
_steps = new Steps(); _steps = new Steps();
_options = o =>
{
o.Authority = _identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
} }
[Fact] [Fact]
@ -45,14 +56,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -79,7 +83,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888")) .And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
@ -104,14 +108,7 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AuthenticationProviderKey = "Test"
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -137,7 +134,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888")) .And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
@ -161,15 +158,9 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
Provider = "IdentityServer", },
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
} }
} }
}; };
@ -178,7 +169,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
@ -202,15 +193,9 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>{ "api", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
Provider = "IdentityServer", },
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
} }
} }
}; };
@ -219,7 +204,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
@ -259,7 +244,7 @@ namespace Ocelot.AcceptanceTests
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddTemporarySigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource

View File

@ -40,7 +40,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -68,7 +68,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -96,7 +96,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -124,7 +124,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -152,7 +152,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -180,7 +180,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/PRODUCTS/1"))
@ -188,16 +188,17 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()
.UseUrls(url) .UseUrls(baseUrl)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(baseUrl)
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath);
app.Run(async context => app.Run(async context =>
{ {
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -24,10 +25,20 @@ namespace Ocelot.AcceptanceTests
private IWebHost _servicebuilder; private IWebHost _servicebuilder;
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private readonly Steps _steps; private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl = "http://localhost:52888";
public ClaimsToHeadersForwardingTests() public ClaimsToHeadersForwardingTests()
{ {
_steps = new Steps(); _steps = new Steps();
_options = o =>
{
o.Authority = _identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
} }
[Fact] [Fact]
@ -59,17 +70,11 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:52888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -86,7 +91,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:52888")) .And(x => _steps.GivenIHaveAToken("http://localhost:52888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
@ -133,7 +138,7 @@ namespace Ocelot.AcceptanceTests
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddTemporarySigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -24,10 +25,20 @@ namespace Ocelot.AcceptanceTests
private IWebHost _servicebuilder; private IWebHost _servicebuilder;
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private readonly Steps _steps; private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl = "http://localhost:57888";
public ClaimsToQueryStringForwardingTests() public ClaimsToQueryStringForwardingTests()
{ {
_steps = new Steps(); _steps = new Steps();
_options = o =>
{
o.Authority = _identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
} }
[Fact] [Fact]
@ -59,17 +70,11 @@ namespace Ocelot.AcceptanceTests
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:57888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}, },
AddQueriesToRequest = AddQueriesToRequest =
{ {
@ -86,7 +91,7 @@ namespace Ocelot.AcceptanceTests
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:57888")) .And(x => _steps.GivenIHaveAToken("http://localhost:57888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
@ -140,7 +145,7 @@ namespace Ocelot.AcceptanceTests
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddTemporarySigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource

View File

@ -75,7 +75,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/ClientRateLimit")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1)) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit",1))
@ -128,7 +128,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/ClientRateLimit")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/ClientRateLimit"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4)) .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4))
@ -137,16 +137,17 @@ namespace Ocelot.AcceptanceTests
} }
private void GivenThereIsAServiceRunningOn(string url) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()
.UseUrls(url) .UseUrls(baseUrl)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(baseUrl)
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath);
app.Run(context => app.Run(context =>
{ {
_counterOne++; _counterOne++;

View File

@ -3,15 +3,12 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Consul;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -105,9 +102,7 @@ namespace Ocelot.AcceptanceTests
var json = reader.ReadToEnd(); var json = reader.ReadToEnd();
var settings = new JsonSerializerSettings(); _config = JsonConvert.DeserializeObject<OcelotConfiguration>(json);
settings.Converters.Add(new AuthenticationConfigConverter());
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
var response = JsonConvert.SerializeObject(true); var response = JsonConvert.SerializeObject(true);

View File

@ -119,7 +119,7 @@ namespace Ocelot.AcceptanceTests
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "41879/", DownstreamPathTemplate = "/41879/",
DownstreamPort = 41879, DownstreamPort = 41879,
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",

View File

@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<AssemblyName>Ocelot.AcceptanceTests</AssemblyName> <AssemblyName>Ocelot.AcceptanceTests</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>Ocelot.AcceptanceTests</PackageId> <PackageId>Ocelot.AcceptanceTests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers> <RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@ -30,28 +30,23 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" /> <PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="1.1.1" /> <PackageReference Include="Shouldly" Version="3.0.0-beta0003" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.1" />
<PackageReference Include="Shouldly" Version="2.8.2" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" /> <PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Consul" Version="0.7.2.1" /> <PackageReference Include="Consul" Version="0.7.2.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit" Version="2.3.0-beta2-build3683" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -98,7 +98,7 @@ namespace Ocelot.AcceptanceTests
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
DownstreamPort = 51880, DownstreamPort = 51880,
UpstreamPathTemplate = "working", UpstreamPathTemplate = "/working",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -15,6 +16,7 @@ namespace Ocelot.AcceptanceTests
{ {
private IWebHost _builder; private IWebHost _builder;
private readonly Steps _steps; private readonly Steps _steps;
private string _downstreamPath;
public RoutingTests() public RoutingTests()
{ {
@ -50,7 +52,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -59,6 +61,47 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void bug()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/v1/vacancy",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/vacancy/",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore",
LoadBalancer = "LeastConnection"
},
new FileReRoute
{
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/vacancy/{vacancyId}",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore",
LoadBalancer = "LeastConnection"
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact] [Fact]
public void should_return_response_200_when_path_missing_forward_slash_as_first_char() public void should_return_response_200_when_path_missing_forward_slash_as_first_char()
{ {
@ -68,7 +111,7 @@ namespace Ocelot.AcceptanceTests
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "api/products", DownstreamPathTemplate = "/api/products",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
DownstreamPort = 51879, DownstreamPort = 51879,
@ -78,7 +121,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -106,7 +149,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -134,7 +177,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
@ -162,7 +205,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -196,7 +239,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/products", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
@ -223,7 +266,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
@ -232,6 +275,34 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_add_trailing_slash_to_downstream_url()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/{productId}",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
.Then(x => ThenTheDownstreamUrlPathShouldBe("/api/products/1"))
.BDDfy();
}
[Fact] [Fact]
public void should_return_response_201_with_simple_url() public void should_return_response_201_with_simple_url()
{ {
@ -251,7 +322,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 201, string.Empty)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
@ -279,7 +350,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-"))
@ -307,7 +378,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/myApp1Name/api/products/1", 200, "Some Product")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/myApp1Name/api/products/1", 200, "Some Product"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1"))
@ -335,7 +406,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 201, string.Empty)) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
@ -363,7 +434,7 @@ namespace Ocelot.AcceptanceTests
} }
}; };
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -372,18 +443,59 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) [Fact]
public void should_return_404_when_calling_upstream_route_with_no_matching_downstream_re_route_github_issue_134()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/v1/vacancy",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/vacancy/",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore",
LoadBalancer = "LeastConnection"
},
new FileReRoute
{
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/vacancy/{vacancyId}",
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
ServiceName = "botCore",
LoadBalancer = "LeastConnection"
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{ {
_builder = new WebHostBuilder() _builder = new WebHostBuilder()
.UseUrls(url) .UseUrls(baseUrl)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url)
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath);
app.Run(async context => app.Run(async context =>
{ {
_downstreamPath = context.Request.PathBase.Value;
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
@ -393,6 +505,11 @@ namespace Ocelot.AcceptanceTests
_builder.Start(); _builder.Start();
} }
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
}
public void Dispose() public void Dispose()
{ {
_builder?.Dispose(); _builder?.Dispose();

View File

@ -8,6 +8,8 @@ using System.Net.Http.Headers;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CacheManager.Core; using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -17,7 +19,6 @@ using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.ManualTest;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
using Shouldly; using Shouldly;
@ -86,6 +87,26 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
public void GivenOcelotIsRunning(Action<IdentityServerAuthenticationOptions> options, string authenticationProviderKey)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s =>
{
s.AddSingleton(_webHostBuilder);
s.AddAuthentication()
.AddIdentityServerAuthentication(authenticationProviderKey, options);
});
_ocelotServer = new TestServer(_webHostBuilder
.UseStartup<Startup>());
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfig(ConsulRegistryConfiguration consulConfig) public void GivenOcelotIsRunningUsingConsulToStoreConfig(ConsulRegistryConfiguration consulConfig)
{ {
_webHostBuilder = new WebHostBuilder(); _webHostBuilder = new WebHostBuilder();
@ -135,9 +156,7 @@ namespace Ocelot.AcceptanceTests
.AddEnvironmentVariables(); .AddEnvironmentVariables();
var configuration = builder.Build(); var configuration = builder.Build();
_webHostBuilder = new WebHostBuilder(); _webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s => _webHostBuilder.ConfigureServices(s =>
{ {
s.AddSingleton(_webHostBuilder); s.AddSingleton(_webHostBuilder);
@ -160,7 +179,7 @@ namespace Ocelot.AcceptanceTests
}) })
.ConfigureLogging(l => .ConfigureLogging(l =>
{ {
l.AddConsole(configuration.GetSection("Logging")); l.AddConsole();
l.AddDebug(); l.AddDebug();
}) })
.Configure(a => .Configure(a =>
@ -362,4 +381,42 @@ namespace Ocelot.AcceptanceTests
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected); _response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
} }
} }
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
services.AddOcelot(Configuration, settings);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
} }

View File

@ -2,7 +2,8 @@
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<AssemblyName>Ocelot.Benchmarks</AssemblyName> <AssemblyName>Ocelot.Benchmarks</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>Ocelot.Benchmarks</PackageId> <PackageId>Ocelot.Benchmarks</PackageId>
@ -17,7 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.2" /> <PackageReference Include="BenchmarkDotNet" Version="0.10.9" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -9,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Cache; using Ocelot.Cache;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.ManualTest;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;

View File

@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<AssemblyName>Ocelot.IntegrationTests</AssemblyName> <AssemblyName>Ocelot.IntegrationTests</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>Ocelot.IntegrationTests</PackageId> <PackageId>Ocelot.IntegrationTests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@ -30,27 +30,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" /> <PackageReference Include="IdentityServer4" Version="2.0.2" />
<PackageReference Include="xunit" Version="2.3.0-beta2-build3683" /> <PackageReference Include="Shouldly" Version="3.0.0-beta0003" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="1.1.1" />
<PackageReference Include="IdentityServer4" Version="1.5.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" />
<PackageReference Include="Shouldly" Version="2.8.2" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" /> <PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Consul" Version="0.7.2.1" /> <PackageReference Include="Consul" Version="0.7.2.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,51 @@
using System;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.IntegrationTests
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
services.AddOcelot(Configuration, settings);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
app.UseOcelot().Wait();
}
}
}

View File

@ -1,15 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.ManualTest;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;

View File

@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Ocelot.ManualTest</AssemblyName> <AssemblyName>Ocelot.ManualTest</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>Ocelot.ManualTest</PackageId> <PackageId>Ocelot.ManualTest</PackageId>
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers> <RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -17,24 +17,27 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="configuration.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" /> <ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" /> <PackageReference Include="Consul" Version="0.7.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" /> <PackageReference Include="Polly" Version="5.3.1" />
<PackageReference Include="Consul" Version="0.7.2.1" />
<PackageReference Include="Polly" Version="5.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -10,175 +10,362 @@
"e8825dc3-4137-99a7-0000-ef5786610dc3", "e8825dc3-4137-99a7-0000-ef5786610dc3",
"fddfc4fa-5114-69e3-4744-203ed71a526b", "fddfc4fa-5114-69e3-4744-203ed71a526b",
"c45d30d7-d9c4-fa05-8110-d6e769bb6ff9", "c45d30d7-d9c4-fa05-8110-d6e769bb6ff9",
"4684c2fa-f38c-c193-5f55-bf563a1978c6" "4684c2fa-f38c-c193-5f55-bf563a1978c6",
"37bfa9f1-fe29-6a68-e558-66d125d2c96f",
"5f308240-79e3-cf74-7a6b-fe462f0d54f1",
"178f16da-c61b-c881-1c33-9d64a56851a4"
], ],
"folders": [], "folders": [],
"timestamp": 1477767328599, "folders_order": [],
"timestamp": 0,
"owner": "212120", "owner": "212120",
"public": false, "public": false,
"requests": [ "requests": [
{ {
"folder": null,
"id": "09af8dda-a9cb-20d2-5ee3-0a3023773a1a", "id": "09af8dda-a9cb-20d2-5ee3-0a3023773a1a",
"headers": "",
"url": "http://localhost:5000/comments?postId=1",
"pathVariables": {},
"preRequestScript": null,
"method": "GET",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"data": null,
"dataMode": "params",
"name": "GET http://localhost:5000/comments?postId=1", "name": "GET http://localhost:5000/comments?postId=1",
"description": "",
"descriptionFormat": "html",
"time": 1477768105592,
"version": 2,
"responses": [],
"tests": null,
"currentHelper": "normal",
"helperAttributes": {}
},
{
"id": "4684c2fa-f38c-c193-5f55-bf563a1978c6",
"headers": "",
"url": "http://localhost:5000/posts/1",
"pathVariables": {},
"preRequestScript": null,
"method": "DELETE",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"data": null,
"dataMode": "params", "dataMode": "params",
"name": "DELETE http://localhost:5000/posts/1", "data": null,
"description": "", "rawModeData": null,
"descriptionFormat": "html", "descriptionFormat": "html",
"time": 1477768404376, "description": "",
"version": 2,
"responses": [],
"tests": null,
"currentHelper": "normal",
"helperAttributes": {}
},
{
"id": "a1c95935-ed18-d5dc-bcb8-a3db8ba1934f",
"headers": "", "headers": "",
"url": "http://localhost:5000/posts",
"pathVariables": {},
"preRequestScript": null,
"method": "GET", "method": "GET",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375", "pathVariables": {},
"data": null, "url": "http://localhost:5000/comments?postId=1",
"dataMode": "params", "preRequestScript": null,
"name": "GET http://localhost:5000/posts",
"description": "",
"descriptionFormat": "html",
"time": 1477768007806,
"version": 2,
"responses": [],
"tests": null, "tests": null,
"currentHelper": "normal", "currentHelper": "normal",
"helperAttributes": {} "helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
}, },
{ {
"id": "c4494401-3985-a5bf-71fb-6e4171384ac6", "id": "178f16da-c61b-c881-1c33-9d64a56851a4",
"headers": "", "headers": "Authorization: Bearer {{AccessToken}}\n",
"url": "http://localhost:5000/posts/1/comments", "headerData": [
"pathVariables": {}, {
"key": "Authorization",
"value": "Bearer {{AccessToken}}",
"enabled": true,
"description": ""
}
],
"url": "http://localhost:5000/administration/configuration",
"folder": null,
"queryParams": [],
"preRequestScript": null, "preRequestScript": null,
"pathVariables": {},
"pathVariableData": [],
"method": "GET", "method": "GET",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"data": null, "data": null,
"dataMode": "params", "dataMode": "params",
"name": "GET http://localhost:5000/posts/1/comments",
"description": "",
"descriptionFormat": "html",
"time": 1477768043524,
"version": 2,
"responses": [],
"tests": null, "tests": null,
"currentHelper": "normal", "currentHelper": "normal",
"helperAttributes": {} "helperAttributes": "{}",
}, "time": 1508849878025,
{ "name": "GET http://localhost:5000/admin/configuration",
"id": "c45d30d7-d9c4-fa05-8110-d6e769bb6ff9", "description": "",
"headers": "",
"url": "http://localhost:5000/posts/1",
"pathVariables": {},
"preRequestScript": null,
"method": "PATCH",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375", "collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"data": [],
"dataMode": "raw",
"name": "PATCH http://localhost:5000/posts/1",
"description": "",
"descriptionFormat": "html",
"time": 1477768379775,
"version": 2,
"responses": [], "responses": [],
"tests": null, "isFromCollection": true,
"currentHelper": "normal", "collectionRequestId": "178f16da-c61b-c881-1c33-9d64a56851a4",
"helperAttributes": {}, "rawModeData": null,
"rawModeData": "{\n \"title\": \"gfdgsgsdgsdfgsdfgdfg\",\n}" "descriptionFormat": null
}, },
{ {
"id": "e8825dc3-4137-99a7-0000-ef5786610dc3", "id": "37bfa9f1-fe29-6a68-e558-66d125d2c96f",
"headers": "", "headers": "",
"url": "http://localhost:5000/posts", "headerData": [],
"pathVariables": {}, "url": "http://localhost:5000/administration/connect/token",
"folder": null,
"queryParams": [],
"preRequestScript": null, "preRequestScript": null,
"pathVariables": {},
"pathVariableData": [],
"method": "POST", "method": "POST",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375", "data": [
"data": [], {
"dataMode": "raw", "key": "client_id",
"name": "POST http://localhost:5000/posts/1", "value": "admin",
"description": "", "type": "text",
"descriptionFormat": "html", "enabled": true
"time": 1477768186023,
"version": 2,
"responses": [],
"tests": null,
"currentHelper": "normal",
"helperAttributes": {},
"rawModeData": "{\n \"userId\": 1,\n \"title\": \"test\",\n \"body\": \"test\"\n}"
}, },
{ {
"id": "ea0ed57a-2cb9-8acc-47dd-006b8db2f1b2", "key": "client_secret",
"headers": "", "value": "secret",
"url": "http://localhost:5000/posts/1", "type": "text",
"pathVariables": {}, "enabled": true
"preRequestScript": null, },
"method": "GET", {
"key": "scope",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "username",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "password",
"value": "secret",
"type": "text",
"enabled": true
},
{
"key": "grant_type",
"value": "password",
"type": "text",
"enabled": true
}
],
"dataMode": "params",
"tests": "var jsonData = JSON.parse(responseBody);\npostman.setGlobalVariable(\"AccessToken\", jsonData.access_token);\npostman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);",
"currentHelper": "normal",
"helperAttributes": "{}",
"time": 1506359585080,
"name": "POST http://localhost:5000/admin/connect/token copy",
"description": "",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375", "collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"responses": [],
"isFromCollection": true,
"collectionRequestId": "37bfa9f1-fe29-6a68-e558-66d125d2c96f",
"rawModeData": null,
"descriptionFormat": null
},
{
"folder": null,
"id": "4684c2fa-f38c-c193-5f55-bf563a1978c6",
"name": "DELETE http://localhost:5000/posts/1",
"dataMode": "params",
"data": null,
"rawModeData": null,
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "DELETE",
"pathVariables": {},
"url": "http://localhost:5000/posts/1",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"id": "5f308240-79e3-cf74-7a6b-fe462f0d54f1",
"headers": "Authorization: Bearer {{AccessToken}}\n",
"headerData": [
{
"key": "Authorization",
"value": "Bearer {{AccessToken}}",
"description": "",
"enabled": true
}
],
"url": "http://localhost:5000/administration/.well-known/openid-configuration",
"folder": null,
"queryParams": [],
"preRequestScript": null,
"pathVariables": {},
"pathVariableData": [],
"method": "GET",
"data": null, "data": null,
"dataMode": "params", "dataMode": "params",
"name": "GET http://localhost:5000/posts/1",
"description": "",
"descriptionFormat": "html",
"time": 1477768023989,
"version": 2,
"responses": [],
"tests": null,
"currentHelper": "normal",
"helperAttributes": {}
},
{
"id": "fddfc4fa-5114-69e3-4744-203ed71a526b",
"headers": "",
"url": "http://localhost:5000/posts/1",
"pathVariables": {},
"preRequestScript": null,
"method": "PUT",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"data": [],
"dataMode": "raw",
"name": "PUT http://localhost:5000/posts/1",
"description": "",
"descriptionFormat": "html",
"time": 1477768307036,
"version": 2,
"responses": [],
"tests": null, "tests": null,
"currentHelper": "normal", "currentHelper": "normal",
"helperAttributes": {}, "helperAttributes": {},
"rawModeData": "{\n \"userId\": 1,\n \"title\": \"test\",\n \"body\": \"test\"\n}" "time": 1508849923518,
"name": "GET http://localhost:5000/admin/.well-known/openid-configuration",
"description": "",
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375",
"responses": []
},
{
"folder": null,
"id": "a1c95935-ed18-d5dc-bcb8-a3db8ba1934f",
"name": "GET http://localhost:5000/posts",
"dataMode": "params",
"data": [
{
"key": "client_id",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "client_secret",
"value": "secret",
"type": "text",
"enabled": true
},
{
"key": "scope",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "username",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "password",
"value": "admin",
"type": "text",
"enabled": true
},
{
"key": "grant_type",
"value": "password",
"type": "text",
"enabled": true
}
],
"rawModeData": null,
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "POST",
"pathVariables": {},
"url": "http://localhost:5000/admin/configuration",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"folder": null,
"id": "c4494401-3985-a5bf-71fb-6e4171384ac6",
"name": "GET http://localhost:5000/posts/1/comments",
"dataMode": "params",
"data": null,
"rawModeData": null,
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "GET",
"pathVariables": {},
"url": "http://localhost:5000/posts/1/comments",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"folder": null,
"id": "c45d30d7-d9c4-fa05-8110-d6e769bb6ff9",
"name": "PATCH http://localhost:5000/posts/1",
"dataMode": "raw",
"data": [],
"rawModeData": "{\n \"title\": \"gfdgsgsdgsdfgsdfgdfg\",\n}",
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "PATCH",
"pathVariables": {},
"url": "http://localhost:5000/posts/1",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"folder": null,
"id": "e8825dc3-4137-99a7-0000-ef5786610dc3",
"name": "POST http://localhost:5000/posts/1",
"dataMode": "raw",
"data": [],
"rawModeData": "{\n \"userId\": 1,\n \"title\": \"test\",\n \"body\": \"test\"\n}",
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "POST",
"pathVariables": {},
"url": "http://localhost:5000/posts",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"folder": null,
"id": "ea0ed57a-2cb9-8acc-47dd-006b8db2f1b2",
"name": "GET http://localhost:5000/posts/1",
"dataMode": "params",
"data": null,
"rawModeData": null,
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "GET",
"pathVariables": {},
"url": "http://localhost:5000/posts/1",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
},
{
"folder": null,
"id": "fddfc4fa-5114-69e3-4744-203ed71a526b",
"name": "PUT http://localhost:5000/posts/1",
"dataMode": "raw",
"data": [],
"rawModeData": "{\n \"userId\": 1,\n \"title\": \"test\",\n \"body\": \"test\"\n}",
"descriptionFormat": "html",
"description": "",
"headers": "",
"method": "PUT",
"pathVariables": {},
"url": "http://localhost:5000/posts/1",
"preRequestScript": null,
"tests": null,
"currentHelper": "normal",
"helperAttributes": "{}",
"queryParams": [],
"headerData": [],
"pathVariableData": [],
"responses": [],
"collectionId": "4dbde9fe-89f5-be35-bb9f-d3b438e16375"
} }
] ]
} }

View File

@ -9,17 +9,14 @@ namespace Ocelot.ManualTest
public static void Main(string[] args) public static void Main(string[] args)
{ {
IWebHostBuilder builder = new WebHostBuilder(); IWebHostBuilder builder = new WebHostBuilder();
builder.ConfigureServices(s => { builder.ConfigureServices(s => {
s.AddSingleton(builder); s.AddSingleton(builder);
}); });
builder.UseKestrel() builder.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>(); .UseStartup<Startup>();
var host = builder.Build(); var host = builder.Build();
host.Run(); host.Run();
} }
} }

View File

@ -38,6 +38,13 @@ namespace Ocelot.ManualTest
.WithDictionaryHandle(); .WithDictionaryHandle();
}; };
services.AddAuthentication()
.AddJwtBearer("TestKey", x =>
{
x.Authority = "test";
x.Audience = "test";
});
services.AddOcelot(Configuration, settings); services.AddOcelot(Configuration, settings);
} }

View File

@ -13,17 +13,11 @@
"TimeoutValue": 5000 "TimeoutValue": 5000
}, },
"AuthenticationOptions": { "AuthenticationOptions": {
"Provider": "IdentityServer", "AuthenticationProviderKey": "TestKey",
"AllowedScopes": [ "AllowedScopes": [
"openid", "openid",
"offline_access" "offline_access"
], ]
"IdentityServerConfig": {
"ProviderRootUrl": "http://localhost:52888",
"ApiName": "api",
"ApiSecret": "secret",
"RequireHttps": false
}
}, },
"AddHeadersToRequest": { "AddHeadersToRequest": {
"CustomerId": "Claims[CustomerId] > value", "CustomerId": "Claims[CustomerId] > value",
@ -312,6 +306,6 @@
"GlobalConfiguration": { "GlobalConfiguration": {
"RequestIdKey": "OcRequestId", "RequestIdKey": "OcRequestId",
"AdministrationPath": "/admin" "AdministrationPath": "/administration"
} }
} }

Binary file not shown.

View File

@ -0,0 +1 @@
{"KeyId":"6aad821b3e366a0189ea6c9741eae6ba","Parameters":{"D":"RcfIF/iJ8qnKpHlaJCa9Qz+iN9Z655mfW0B/CycZx0WDQwQtjYNF+ijEkqfpGC3TJ9n19vXHdDEGfONxHwTtgS6PP/VIYYmql7OfCn+tkZUvMeIXykfEXFoNoWJXlT3eMI1JWyZpT/dZJLtmdeY09csDU/LjXTlyFrljW361T0NR/azAHqEfeuoKhqaJ2klzTzjif4xO5kMcTBHVyxrZm0+cbowsKPjI1QRh5xXsst8EDrM7rXStz4enneNaNbvP1nmWx++F7zn+5/WBDcPJVnL1HiyAzMAHj+oXG2JwDizO4RJxLbvQa2Y2jzoDp/qc++s2HWFo1PmuUnOzNIQjyQ==","DP":"x3VwsILF1yo8puLB6TOcb4hMWngz8rqjBl3dty4E3Un7UexVh+NkqTiSXWZNern6Ka6gE8CpdDXhQiYCFbcnBiSF6FVSpjpQ+Qf9PeRj5HipJF+DzGyEOTzwOiBjb6s5CvPUQWvJiqQoP1D+1V+X/+C0zug8+Df73UyHA7uieKc=","DQ":"aaZu9GfgKqUiy0uwPkmnLcwIEDqRlLG14c23OOoEqNRHK9T3OwUKEJ1qK0mbMKIVTwklyiC7uZHqMijIqk0LovTL4nI3LRlDkRjhUsIuubZZOAw7sYYemBUl+wEB/VZofaJ7H/CYtCUXyJhND2DFbTjzgeg3uWoMpyMDHuH/9Pc=","Exponent":"AQAB","InverseQ":"YRV4QA6rwB9BEHjzh5Wk3TcSS1CrwJWVopj/qC1amXxhtM3aMb4ZfKk7XoynLqHyQ86rB2p7dPNP2GL+fIWbt4h/ESO9JqOlM/bVXpdyCvIwchAL82hfHb4FRv+V+J2cksaIA+bHt7ye9n/XSmSr8v0WsjxN2qHzdla3t+J0c1I=","Modulus":"xLJZzQyYbJAqymvvJeig9H4cfXEy3t0KVnRuUumdSBzU/3F7q1vCDBkXLqs8icEv9ZL4MUgDzzSjjYJpVXzvC24L1My3NLhSSZOCGrhSHCx98+cAgrb71tirXXsiBMXKeGhnJ05KopHtPRJVxBvd3d3Kee957y86g1Sbho1XxwWsrzVu5E7YZS+NkJycHkiUseMKeQ+tMLbPoFZPu5EqrrsSWuDjb7XNUjJViyGaOtvL2SQ9QtvDu006fe4m0VVw71ycSt2ReAmlA+EgCFsyLYBIoAhlk7k+lKyYO3a/8E0bzltB6MGaRaGJMB0C9pSxAfGSmSpIi2OUy0YIpIoDoQ==","P":"ydx6DVoXf3DgS6WZtrR82xNf12kLD5cGUToPwwIjjX5oQywOhGXOV4GCrqISDff2bosrPvleBfuJ5KH9KRVAaEjh1At554Bq+Nw8cc/1mTXEOSKENDtA9GjkpthR0QW1FDFRR5Tc8sRuoBpulN1rJIDIkfEkqwlpugFmk2UrDk8=","Q":"+XNIV8qMorQ11C1fVj4L91wufF4NqVqCdm/PN3f+xZ5UWoiCOil+njRuIL09ZifEwy3fgqD06Fu/SvaqMODyKAzA+RMUJU0sk92aOzAhKiGBk38sEvEuDUKZYNJm5NLjo9XXBG8DQzSUPvmIFLaMCloA95Ozie0mJcrXcimCww8="}}

View File

@ -1,100 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Authentication.Handler;
using Ocelot.Authentication.Handler.Creator;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Configuration.Builder;
using Ocelot.Errors;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using AuthenticationOptions = Ocelot.Configuration.AuthenticationOptions;
namespace Ocelot.UnitTests.Authentication
{
public class AuthenticationHandlerFactoryTests
{
private readonly IAuthenticationHandlerFactory _authenticationHandlerFactory;
private readonly Mock<IApplicationBuilder> _app;
private readonly Mock<IAuthenticationHandlerCreator> _creator;
private AuthenticationOptions _authenticationOptions;
private Response<AuthenticationHandler> _result;
public AuthenticationHandlerFactoryTests()
{
_app = new Mock<IApplicationBuilder>();
_creator = new Mock<IAuthenticationHandlerCreator>();
_authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object);
}
[Theory]
[InlineData("IdentityServer")]
[InlineData("Jwt")]
public void should_return_access_token_handler(string provider)
{
var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider(provider)
.Build();
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
.And(x => x.GivenTheCreatorReturns())
.When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenTheHandlerIsReturned(provider))
.BDDfy();
}
[Fact]
public void should_return_error_if_cannot_create_handler()
{
var authenticationOptions = new AuthenticationOptionsBuilder()
.Build();
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
.And(x => x.GivenTheCreatorReturnsAnError())
.When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenAnErrorResponseIsReturned())
.BDDfy();
}
private void GivenTheAuthenticationOptionsAre(AuthenticationOptions authenticationOptions)
{
_authenticationOptions = authenticationOptions;
}
private void GivenTheCreatorReturnsAnError()
{
_creator
.Setup(x => x.Create(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
.Returns(new ErrorResponse<RequestDelegate>(new List<Error>
{
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
}));
}
private void GivenTheCreatorReturns()
{
_creator
.Setup(x => x.Create(It.IsAny<IApplicationBuilder>(), It.IsAny<AuthenticationOptions>()))
.Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask));
}
private void WhenIGetFromTheFactory()
{
_result = _authenticationHandlerFactory.Get(_app.Object, _authenticationOptions);
}
private void ThenTheHandlerIsReturned(string expected)
{
_result.Data.Provider.ShouldBe(expected);
}
private void ThenAnErrorResponseIsReturned()
{
_result.IsError.ShouldBeTrue();
}
}
}

View File

@ -2,11 +2,9 @@
{ {
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Authentication.Middleware; using Ocelot.Authentication.Middleware;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
@ -19,13 +17,10 @@
public class AuthenticationMiddlewareTests : ServerHostedMiddlewareTest public class AuthenticationMiddlewareTests : ServerHostedMiddlewareTest
{ {
private readonly Mock<IAuthenticationHandlerFactory> _authFactory;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
public AuthenticationMiddlewareTests() public AuthenticationMiddlewareTests()
{ {
_authFactory = new Mock<IAuthenticationHandlerFactory>();
GivenTheTestServerIsConfigured(); GivenTheTestServerIsConfigured();
} }
@ -45,7 +40,6 @@
{ {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging(); services.AddLogging();
services.AddSingleton(_authFactory.Object);
services.AddSingleton(ScopedRepository.Object); services.AddSingleton(ScopedRepository.Object);
} }

View File

@ -1,133 +1,133 @@
using System.Collections.Generic; // using System.Collections.Generic;
using Ocelot.Configuration; // using Ocelot.Configuration;
using Ocelot.Configuration.Builder; // using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator; // using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File; // using Ocelot.Configuration.File;
using Shouldly; // using Shouldly;
using TestStack.BDDfy; // using TestStack.BDDfy;
using Xunit; // using Xunit;
namespace Ocelot.UnitTests.Configuration // namespace Ocelot.UnitTests.Configuration
{ // {
public class AuthenticationOptionsCreatorTests // public class AuthenticationOptionsCreatorTests
{ // {
private readonly AuthenticationOptionsCreator _authOptionsCreator; // private readonly AuthenticationOptionsCreator _authOptionsCreator;
private FileReRoute _fileReRoute; // private FileReRoute _fileReRoute;
private AuthenticationOptions _result; // private AuthenticationOptions _result;
public AuthenticationOptionsCreatorTests() // public AuthenticationOptionsCreatorTests()
{ // {
_authOptionsCreator = new AuthenticationOptionsCreator(new AuthenticationProviderConfigCreator()); // _authOptionsCreator = new AuthenticationOptionsCreator(new AuthenticationProviderConfigCreator());
} // }
[Fact] // [Fact]
public void should_return_auth_options() // public void should_return_auth_options()
{ // {
var fileReRoute = new FileReRoute() // var fileReRoute = new FileReRoute()
{ // {
AuthenticationOptions = new FileAuthenticationOptions // AuthenticationOptions = new FileAuthenticationOptions
{ // {
Provider = "Geoff", // Provider = "Geoff",
IdentityServerConfig = new FileIdentityServerConfig() // IdentityServerConfig = new FileIdentityServerConfig()
{ // {
ProviderRootUrl = "http://www.bbc.co.uk/", // ProviderRootUrl = "http://www.bbc.co.uk/",
ApiName = "Laura", // ApiName = "Laura",
RequireHttps = true, // RequireHttps = true,
ApiSecret = "secret" // ApiSecret = "secret"
}, // },
AllowedScopes = new List<string> { "cheese" }, // AllowedScopes = new List<string> { "cheese" },
} // }
}; // };
var authenticationConfig = new IdentityServerConfigBuilder() // var authenticationConfig = new IdentityServerConfigBuilder()
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ProviderRootUrl) // .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiName) // .WithApiName(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.IdentityServerConfig.RequireHttps) // .WithRequireHttps(fileReRoute.AuthenticationOptions.IdentityServerConfig.RequireHttps)
.WithApiSecret(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiSecret) // .WithApiSecret(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiSecret)
.Build(); // .Build();
var expected = new AuthenticationOptionsBuilder() // var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) // .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) // .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig) // .WithConfig(authenticationConfig)
.Build(); // .Build();
this.Given(x => x.GivenTheFollowing(fileReRoute)) // this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions()) // .When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingIdentityServerConfigIsReturned(expected)) // .Then(x => x.ThenTheFollowingIdentityServerConfigIsReturned(expected))
.BDDfy(); // .BDDfy();
} // }
[Fact] // [Fact]
public void should_return_Jwt_auth_options() // public void should_return_Jwt_auth_options()
{ // {
var fileReRoute = new FileReRoute() // var fileReRoute = new FileReRoute()
{ // {
AuthenticationOptions = new FileAuthenticationOptions // AuthenticationOptions = new FileAuthenticationOptions
{ // {
Provider = "Jwt", // Provider = "Jwt",
JwtConfig = new FileJwtConfig() // JwtConfig = new FileJwtConfig()
{ // {
Audience = "Audience", // Audience = "Audience",
Authority = "Authority" // Authority = "Authority"
}, // },
AllowedScopes = new List<string> { "cheese" } // AllowedScopes = new List<string> { "cheese" }
} // }
}; // };
var authenticationConfig = new JwtConfigBuilder() // var authenticationConfig = new JwtConfigBuilder()
.WithAudience(fileReRoute.AuthenticationOptions?.JwtConfig?.Audience) // .WithAudience(fileReRoute.AuthenticationOptions?.JwtConfig?.Audience)
.WithAuthority(fileReRoute.AuthenticationOptions?.JwtConfig?.Authority) // .WithAuthority(fileReRoute.AuthenticationOptions?.JwtConfig?.Authority)
.Build(); // .Build();
var expected = new AuthenticationOptionsBuilder() // var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) // .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) // .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig) // .WithConfig(authenticationConfig)
.Build(); // .Build();
this.Given(x => x.GivenTheFollowing(fileReRoute)) // this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions()) // .When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingJwtConfigIsReturned(expected)) // .Then(x => x.ThenTheFollowingJwtConfigIsReturned(expected))
.BDDfy(); // .BDDfy();
} // }
private void GivenTheFollowing(FileReRoute fileReRoute) // private void GivenTheFollowing(FileReRoute fileReRoute)
{ // {
_fileReRoute = fileReRoute; // _fileReRoute = fileReRoute;
} // }
private void WhenICreateTheAuthenticationOptions() // private void WhenICreateTheAuthenticationOptions()
{ // {
_result = _authOptionsCreator.Create(_fileReRoute); // _result = _authOptionsCreator.Create(_fileReRoute);
} // }
private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected) // private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected)
{ // {
_result.AllowedScopes.ShouldBe(expected.AllowedScopes); // _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider); // _result.Provider.ShouldBe(expected.Provider);
var _resultSettings = _result.Config as JwtConfig; // var _resultSettings = _result.Config as JwtConfig;
var expectedSettngs = expected.Config as JwtConfig; // var expectedSettngs = expected.Config as JwtConfig;
_resultSettings.Audience.ShouldBe(expectedSettngs.Audience); // _resultSettings.Audience.ShouldBe(expectedSettngs.Audience);
_resultSettings.Authority.ShouldBe(expectedSettngs.Authority); // _resultSettings.Authority.ShouldBe(expectedSettngs.Authority);
} // }
private void ThenTheFollowingIdentityServerConfigIsReturned(AuthenticationOptions expected) // private void ThenTheFollowingIdentityServerConfigIsReturned(AuthenticationOptions expected)
{ // {
_result.AllowedScopes.ShouldBe(expected.AllowedScopes); // _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider); // _result.Provider.ShouldBe(expected.Provider);
var _resultSettings = _result.Config as IdentityServerConfig; // var _resultSettings = _result.Config as IdentityServerConfig;
var expectedSettngs = expected.Config as IdentityServerConfig; // var expectedSettngs = expected.Config as IdentityServerConfig;
_resultSettings.ProviderRootUrl.ShouldBe(expectedSettngs.ProviderRootUrl); // _resultSettings.ProviderRootUrl.ShouldBe(expectedSettngs.ProviderRootUrl);
_resultSettings.RequireHttps.ShouldBe(expectedSettngs.RequireHttps); // _resultSettings.RequireHttps.ShouldBe(expectedSettngs.RequireHttps);
_resultSettings.ApiName.ShouldBe(expectedSettngs.ApiName); // _resultSettings.ApiName.ShouldBe(expectedSettngs.ApiName);
_resultSettings.ApiSecret.ShouldBe(expectedSettngs.ApiSecret); // _resultSettings.ApiSecret.ShouldBe(expectedSettngs.ApiSecret);
} // }
} // }
} // }

View File

@ -1,4 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator; using Ocelot.Configuration.Validator;
using Ocelot.Responses; using Ocelot.Responses;
@ -13,10 +20,12 @@ namespace Ocelot.UnitTests.Configuration
private readonly IConfigurationValidator _configurationValidator; private readonly IConfigurationValidator _configurationValidator;
private FileConfiguration _fileConfiguration; private FileConfiguration _fileConfiguration;
private Response<ConfigurationValidationResult> _result; private Response<ConfigurationValidationResult> _result;
private Mock<IAuthenticationSchemeProvider> _provider;
public ConfigurationValidationTests() public ConfigurationValidationTests()
{ {
_configurationValidator = new FileConfigurationValidator(); _provider = new Mock<IAuthenticationSchemeProvider>();
_configurationValidator = new FileConfigurationValidator(_provider.Object);
} }
[Fact] [Fact]
@ -48,7 +57,7 @@ namespace Ocelot.UnitTests.Configuration
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "http://asdf.com" UpstreamPathTemplate = "/asdf/"
} }
} }
})) }))
@ -57,6 +66,44 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact]
public void configuration_is_invalid_without_slash_prefix_downstream_path_template()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "api/products/",
UpstreamPathTemplate = "/asdf/"
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.BDDfy();
}
[Fact]
public void configuration_is_invalid_without_slash_prefix_upstream_path_template()
{
this.Given(x => x.GivenAConfiguration(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "api/prod/",
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.BDDfy();
}
[Fact] [Fact]
public void configuration_is_valid_with_valid_authentication_provider() public void configuration_is_valid_with_valid_authentication_provider()
{ {
@ -67,14 +114,15 @@ namespace Ocelot.UnitTests.Configuration
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "http://asdf.com", UpstreamPathTemplate = "/asdf/",
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions()
{ {
Provider = "IdentityServer" AuthenticationProviderKey = "Test"
} }
} }
} }
})) }))
.And(x => x.GivenTheAuthSchemeExists("Test"))
.When(x => x.WhenIValidateTheConfiguration()) .When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsValid()) .Then(x => x.ThenTheResultIsValid())
.BDDfy(); .BDDfy();
@ -90,12 +138,11 @@ namespace Ocelot.UnitTests.Configuration
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "http://asdf.com", UpstreamPathTemplate = "/asdf/",
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions()
{ {
Provider = "BootyBootyBottyRockinEverywhere" AuthenticationProviderKey = "Test"
} } }
}
} }
})) }))
.When(x => x.WhenIValidateTheConfiguration()) .When(x => x.WhenIValidateTheConfiguration())
@ -114,12 +161,12 @@ namespace Ocelot.UnitTests.Configuration
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/api/products/", DownstreamPathTemplate = "/api/products/",
UpstreamPathTemplate = "http://asdf.com" UpstreamPathTemplate = "/asdf/"
}, },
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "http://www.bbc.co.uk", DownstreamPathTemplate = "http://www.bbc.co.uk",
UpstreamPathTemplate = "http://asdf.com" UpstreamPathTemplate = "/asdf/"
} }
} }
})) }))
@ -136,7 +183,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenIValidateTheConfiguration() private void WhenIValidateTheConfiguration()
{ {
_result = _configurationValidator.IsValid(_fileConfiguration); _result = _configurationValidator.IsValid(_fileConfiguration).Result;
} }
private void ThenTheResultIsValid() private void ThenTheResultIsValid()
@ -153,5 +200,31 @@ namespace Ocelot.UnitTests.Configuration
{ {
_result.Data.Errors[0].ShouldBeOfType<T>(); _result.Data.Errors[0].ShouldBeOfType<T>();
} }
private void GivenTheAuthSchemeExists(string name)
{
_provider.Setup(x => x.GetAllSchemesAsync()).ReturnsAsync(new List<AuthenticationScheme>
{
new AuthenticationScheme(name, name, typeof(TestHandler))
});
}
private class TestOptions : AuthenticationSchemeOptions
{
}
private class TestHandler : AuthenticationHandler<TestOptions>
{
public TestHandler(IOptionsMonitor<TestOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var principal = new ClaimsPrincipal();
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name)));
}
}
} }
} }

View File

@ -45,6 +45,7 @@ namespace Ocelot.UnitTests.Configuration
private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator; private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator;
private Mock<IRateLimitOptionsCreator> _rateLimitOptions; private Mock<IRateLimitOptionsCreator> _rateLimitOptions;
private Mock<IRegionCreator> _regionCreator; private Mock<IRegionCreator> _regionCreator;
private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
public FileConfigurationCreatorTests() public FileConfigurationCreatorTests()
{ {
@ -66,6 +67,7 @@ namespace Ocelot.UnitTests.Configuration
_fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>(); _fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>();
_rateLimitOptions = new Mock<IRateLimitOptionsCreator>(); _rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
_regionCreator = new Mock<IRegionCreator>(); _regionCreator = new Mock<IRegionCreator>();
_httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _validator.Object, _logger.Object, _fileConfig.Object, _validator.Object, _logger.Object,
@ -73,7 +75,7 @@ namespace Ocelot.UnitTests.Configuration
_qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object, _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
_serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
_rateLimitOptions.Object, _regionCreator.Object); _rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.Object);
} }
[Fact] [Fact]
@ -444,18 +446,55 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_call_httpHandler_creator()
{
var reRouteOptions = new ReRouteOptionsBuilder()
.Build();
var httpHandlerOptions = new HttpHandlerOptions(true, true);
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHost = "127.0.0.1",
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }
}
},
}))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly())
.BDDfy();
}
private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions)
{
_httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(httpHandlerOptions);
}
private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()
{
_httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once());
}
[Theory] [Theory]
[MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_headers_to_extract(string provider, IAuthenticationConfig config, FileConfiguration fileConfig) public void should_create_with_headers_to_extract(FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider(provider)
.WithAllowedScopes(new List<string>()) .WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -480,23 +519,21 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(provider, expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
[Theory] [Theory]
[MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_authentication_properties(string provider, IAuthenticationConfig config, FileConfiguration fileConfig) public void should_create_with_authentication_properties(FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider(provider)
.WithAllowedScopes(new List<string>()) .WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -516,7 +553,7 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(provider, expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
@ -538,7 +575,7 @@ namespace Ocelot.UnitTests.Configuration
{ {
_validator _validator
.Setup(x => x.IsValid(It.IsAny<FileConfiguration>())) .Setup(x => x.IsValid(It.IsAny<FileConfiguration>()))
.Returns(new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false))); .ReturnsAsync(new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false)));
} }
private void GivenTheConfigIs(FileConfiguration fileConfiguration) private void GivenTheConfigIs(FileConfiguration fileConfiguration)
@ -587,34 +624,13 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
private void ThenTheAuthenticationOptionsAre(string provider, List<ReRoute> expectedReRoutes) private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes)
{ {
for (int i = 0; i < _config.Data.ReRoutes.Count; i++) for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
{ {
var result = _config.Data.ReRoutes[i].AuthenticationOptions; var result = _config.Data.ReRoutes[i].AuthenticationOptions;
var expected = expectedReRoutes[i].AuthenticationOptions; var expected = expectedReRoutes[i].AuthenticationOptions;
result.AllowedScopes.ShouldBe(expected.AllowedScopes); result.AllowedScopes.ShouldBe(expected.AllowedScopes);
result.Provider.ShouldBe(expected.Provider);
if (provider.ToLower() == "identityserver")
{
var config = result.Config as IdentityServerConfig;
var expectedConfig = expected.Config as IdentityServerConfig;
config.ProviderRootUrl.ShouldBe(expectedConfig.ProviderRootUrl);
config.RequireHttps.ShouldBe(expectedConfig.RequireHttps);
config.ApiName.ShouldBe(expectedConfig.ApiName);
config.ApiSecret.ShouldBe(expectedConfig.ApiSecret);
}
else
{
var config = result.Config as JwtConfig;
var expectedConfig = expected.Config as JwtConfig;
config.Audience.ShouldBe(expectedConfig.Audience);
config.Authority.ShouldBe(expectedConfig.Authority);
}
} }
} }

View File

@ -0,0 +1,71 @@
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class HttpHandlerOptionsCreatorTests
{
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
private FileReRoute _fileReRoute;
private HttpHandlerOptions _httpHandlerOptions;
public HttpHandlerOptionsCreatorTests()
{
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator();
}
[Fact]
public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default()
{
var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(true, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_useCookie_and_allowAutoRedirect()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false,
UseCookieContainer = false
}
};
var expectedOptions = new HttpHandlerOptions(false, false);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
private void GivenTheFollowing(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
private void WhenICreateHttpHandlerOptions()
{
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute);
}
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions options)
{
_httpHandlerOptions.ShouldNotBeNull();
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer);
}
}
}

View File

@ -34,9 +34,9 @@ namespace Ocelot.UnitTests.Configuration
ExceptionsAllowedBeforeBreaking = 1, ExceptionsAllowedBeforeBreaking = 1,
TimeoutValue = 1 TimeoutValue = 1
}, },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions()
{ {
Provider = "IdentityServer" AuthenticationProviderKey = "Test"
}, },
RouteClaimsRequirement = new Dictionary<string, string>() RouteClaimsRequirement = new Dictionary<string, string>()
{ {

View File

@ -28,7 +28,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("(?i)/PRODUCTS/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.*/$"))
.BDDfy(); .BDDfy();
} }
@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.Configuration
}; };
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("/PRODUCTS/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$"))
.BDDfy(); .BDDfy();
} }
@ -57,7 +57,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$"))
.BDDfy(); .BDDfy();
} }
@ -72,7 +72,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.When(x => x.WhenICreateTheTemplatePattern()) .When(x => x.WhenICreateTheTemplatePattern())
.Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$")) .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$"))
.BDDfy(); .BDDfy();
} }

View File

@ -36,7 +36,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_return_route() public void should_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>( new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
new List<UrlPathPlaceholderNameAndValue>()))) new List<UrlPathPlaceholderNameAndValue>())))
@ -65,6 +65,39 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_append_slash_to_upstream_url_path()
{
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher"))
.And(x =>x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<UrlPathPlaceholderNameAndValue>>(
new List<UrlPathPlaceholderNameAndValue>())))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{
new ReRouteBuilder()
.WithDownstreamPathTemplate("someDownstreamPath")
.WithUpstreamPathTemplate("someUpstreamPath")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
}, string.Empty
))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder())
.Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(
new List<UrlPathPlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamPathTemplate("someDownstreamPath")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build()
)))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher/"))
.BDDfy();
}
[Fact] [Fact]
public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() public void should_return_route_if_upstream_path_and_upstream_template_are_the_same()
{ {
@ -137,7 +170,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
[Fact] [Fact]
public void should_not_return_route() public void should_not_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/"))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
new ReRouteBuilder() new ReRouteBuilder()
@ -269,6 +302,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
} }
private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath)
{
_mockMatcher
.Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once);
}
private void ThenTheUrlMatcherIsNotCalled() private void ThenTheUrlMatcherIsNotCalled()
{ {
_mockMatcher _mockMatcher

View File

@ -28,6 +28,16 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_match_issue_134()
{
this.Given(x => x.GivenIHaveAUpstreamPath("/api/vacancy/1/"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)/vacancy/.*/$"))
.When(x => x.WhenIMatchThePaths())
.And(x => x.ThenTheResultIsFalse())
.BDDfy();
}
[Fact] [Fact]
public void should_match_forward_slash_only_regex() public void should_match_forward_slash_only_regex()
{ {
@ -42,7 +52,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void should_find_match_when_template_smaller_than_valid_path() public void should_find_match_when_template_smaller_than_valid_path()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("/api/products/2354325435624623464235")) this.Given(x => x.GivenIHaveAUpstreamPath("/api/products/2354325435624623464235"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("/api/products/.*$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/api/products/.*$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.And(x => x.ThenTheResultIsTrue()) .And(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -52,7 +62,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void should_not_find_match() public void should_not_find_match()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("/api/values")) this.Given(x => x.GivenIHaveAUpstreamPath("/api/values"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("/$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.And(x => x.ThenTheResultIsFalse()) .And(x => x.ThenTheResultIsFalse())
.BDDfy(); .BDDfy();
@ -62,7 +72,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url() public void can_match_down_stream_url()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("")) this.Given(x => x.GivenIHaveAUpstreamPath(""))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.And(x => x.ThenTheResultIsTrue()) .And(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -72,7 +82,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_no_slash() public void can_match_down_stream_url_with_no_slash()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api")) this.Given(x => x.GivenIHaveAUpstreamPath("api"))
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api$")) .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -82,7 +92,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_one_slash() public void can_match_down_stream_url_with_one_slash()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/")) this.Given(x => x.GivenIHaveAUpstreamPath("api/"))
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/$")) .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -92,7 +102,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template() public void can_match_down_stream_url_with_downstream_template()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/"))
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/$")) .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -102,7 +112,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template_with_one_place_holder() public void can_match_down_stream_url_with_downstream_template_with_one_place_holder()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1"))
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*$")) .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -112,7 +122,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders() public void can_match_down_stream_url_with_downstream_template_with_two_place_holders()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/2")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/2"))
.Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/.*$")) .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/.*$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -122,7 +132,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template_with_two_place_holders_seperated_by_something() public void can_match_down_stream_url_with_downstream_template_with_two_place_holders_seperated_by_something()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -132,7 +142,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders_seperated_by_something() public void can_match_down_stream_url_with_downstream_template_with_three_place_holders_seperated_by_something()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/123")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/123"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/.*$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/.*$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -142,7 +152,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void can_match_down_stream_url_with_downstream_template_with_three_place_holders() public void can_match_down_stream_url_with_downstream_template_with_three_place_holders()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/")) this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -152,7 +162,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void should_ignore_case_sensitivity() public void should_ignore_case_sensitivity()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/")) this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("(?i)api/product/products/.*/categories/.*/variant/$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)api/product/products/.*/categories/.*/variant/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsTrue()) .Then(x => x.ThenTheResultIsTrue())
.BDDfy(); .BDDfy();
@ -162,7 +172,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher
public void should_respect_case_sensitivity() public void should_respect_case_sensitivity()
{ {
this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/")) this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/"))
.And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/$")) .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$"))
.When(x => x.WhenIMatchThePaths()) .When(x => x.WhenIMatchThePaths())
.Then(x => x.ThenTheResultIsFalse()) .Then(x => x.ThenTheResultIsFalse())
.BDDfy(); .BDDfy();

View File

@ -0,0 +1,124 @@
using Xunit;
using Shouldly;
using Ocelot.Authorisation;
using Ocelot.Infrastructure.Claims.Parser;
using Moq;
using System.Collections.Generic;
using System.Security.Claims;
using Ocelot.Responses;
using TestStack.BDDfy;
using Ocelot.Errors;
namespace Ocelot.UnitTests.Infrastructure
{
public class ScopesAuthoriserTests
{
private ScopesAuthoriser _authoriser;
public Mock<IClaimsParser> _parser;
private ClaimsPrincipal _principal;
private List<string> _allowedScopes;
private Response<bool> _result;
public ScopesAuthoriserTests()
{
_parser = new Mock<IClaimsParser>();
_authoriser = new ScopesAuthoriser(_parser.Object);
}
[Fact]
public void should_return_ok_if_no_allowed_scopes()
{
this.Given(_ => GivenTheFollowing(new ClaimsPrincipal()))
.And(_ => GivenTheFollowing(new List<string>()))
.When(_ => WhenIAuthorise())
.Then(_ => ThenTheFollowingIsReturned(new OkResponse<bool>(true)))
.BDDfy();
}
[Fact]
public void should_return_ok_if_null_allowed_scopes()
{
this.Given(_ => GivenTheFollowing(new ClaimsPrincipal()))
.And(_ => GivenTheFollowing((List<string>)null))
.When(_ => WhenIAuthorise())
.Then(_ => ThenTheFollowingIsReturned(new OkResponse<bool>(true)))
.BDDfy();
}
[Fact]
public void should_return_error_if_claims_parser_returns_error()
{
var fakeError = new FakeError();
this.Given(_ => GivenTheFollowing(new ClaimsPrincipal()))
.And(_ => GivenTheParserReturns(new ErrorResponse<List<string>>(fakeError)))
.And(_ => GivenTheFollowing(new List<string>(){"doesntmatter"}))
.When(_ => WhenIAuthorise())
.Then(_ => ThenTheFollowingIsReturned(new ErrorResponse<bool>(fakeError)))
.BDDfy();
}
[Fact]
public void should_match_scopes_and_return_ok_result()
{
var claimsPrincipal = new ClaimsPrincipal();
var allowedScopes = new List<string>(){"someScope"};
this.Given(_ => GivenTheFollowing(claimsPrincipal))
.And(_ => GivenTheParserReturns(new OkResponse<List<string>>(allowedScopes)))
.And(_ => GivenTheFollowing(allowedScopes))
.When(_ => WhenIAuthorise())
.Then(_ => ThenTheFollowingIsReturned(new OkResponse<bool>(true)))
.BDDfy();
}
[Fact]
public void should_not_match_scopes_and_return_error_result()
{
var fakeError = new FakeError();
var claimsPrincipal = new ClaimsPrincipal();
var allowedScopes = new List<string>(){"someScope"};
var userScopes = new List<string>(){"anotherScope"};
this.Given(_ => GivenTheFollowing(claimsPrincipal))
.And(_ => GivenTheParserReturns(new OkResponse<List<string>>(userScopes)))
.And(_ => GivenTheFollowing(allowedScopes))
.When(_ => WhenIAuthorise())
.Then(_ => ThenTheFollowingIsReturned(new ErrorResponse<bool>(fakeError)))
.BDDfy();
}
private void GivenTheParserReturns(Response<List<string>> response)
{
_parser.Setup(x => x.GetValuesByClaimType(It.IsAny<IEnumerable<Claim>>(), It.IsAny<string>())).Returns(response);
}
private void GivenTheFollowing(ClaimsPrincipal principal)
{
_principal = principal;
}
private void GivenTheFollowing(List<string> allowedScopes)
{
_allowedScopes = allowedScopes;
}
private void WhenIAuthorise()
{
_result = _authoriser.Authorise(_principal, _allowedScopes);
}
private void ThenTheFollowingIsReturned(Response<bool> expected)
{
_result.Data.ShouldBe(expected.Data);
_result.IsError.ShouldBe(expected.IsError);
}
}
public class FakeError : Error
{
public FakeError() : base("fake error", OcelotErrorCode.CannotAddDataError)
{
}
}
}

View File

@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<AssemblyName>Ocelot.UnitTests</AssemblyName> <AssemblyName>Ocelot.UnitTests</AssemblyName>
<PackageId>Ocelot.UnitTests</PackageId> <PackageId>Ocelot.UnitTests</PackageId>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers> <RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@ -32,27 +32,22 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OAuth" Version="1.1.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="1.1.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" /> <PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Moq" Version="4.7.142" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Shouldly" Version="3.0.0-beta0003" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
<PackageReference Include="Moq" Version="4.6.38-alpha" />
<PackageReference Include="Shouldly" Version="2.8.2" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" /> <PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="xunit" Version="2.3.0-beta2-build3683" /> <PackageReference Include="xunit" Version="2.3.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -15,6 +15,7 @@
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Configuration;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest
@ -50,12 +51,13 @@
new ReRouteBuilder() new ReRouteBuilder()
.WithRequestIdKey("LSRequestId") .WithRequestIdKey("LSRequestId")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true))
.Build()); .Build());
this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider()))) .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider()))) .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false)))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy(); .BDDfy();
@ -103,7 +105,11 @@
_request = new OkResponse<Ocelot.Request.Request>(request); _request = new OkResponse<Ocelot.Request.Request>(request);
_requestBuilder _requestBuilder
.Setup(x => x.Build(It.IsAny<HttpRequestMessage>(), It.IsAny<bool>(), It.IsAny<IQoSProvider>())) .Setup(x => x.Build(It.IsAny<HttpRequestMessage>(),
It.IsAny<bool>(),
It.IsAny<IQoSProvider>(),
It.IsAny<bool>(),
It.IsAny<bool>()))
.ReturnsAsync(_request); .ReturnsAsync(_request);
} }

View File

@ -15,6 +15,9 @@
private readonly bool _isQos; private readonly bool _isQos;
private readonly IQoSProvider _qoSProvider; private readonly IQoSProvider _qoSProvider;
private readonly HttpRequestMessage _requestMessage; private readonly HttpRequestMessage _requestMessage;
private readonly bool _useCookieContainer;
private readonly bool _allowAutoRedirect;
private Response<Ocelot.Request.Request> _response; private Response<Ocelot.Request.Request> _response;
public HttpRequestCreatorTests() public HttpRequestCreatorTests()
@ -22,6 +25,9 @@
_requestCreator = new HttpRequestCreator(); _requestCreator = new HttpRequestCreator();
_isQos = true; _isQos = true;
_qoSProvider = new NoQoSProvider(); _qoSProvider = new NoQoSProvider();
_useCookieContainer = false;
_allowAutoRedirect = false;
_requestMessage = new HttpRequestMessage(); _requestMessage = new HttpRequestMessage();
} }
@ -30,12 +36,19 @@
{ {
this.When(x => x.WhenIBuildARequest()) this.When(x => x.WhenIBuildARequest())
.Then(x => x.ThenTheRequestContainsTheRequestMessage()) .Then(x => x.ThenTheRequestContainsTheRequestMessage())
.Then(x => x.ThenTheRequestContainsTheIsQos())
.Then(x => x.ThenTheRequestContainsTheQosProvider())
.Then(x => x.ThenTheRequestContainsUseCookieContainer())
.Then(x => x.ThenTheRequestContainsAllowAutoRedirect())
.BDDfy(); .BDDfy();
} }
private void WhenIBuildARequest() private void WhenIBuildARequest()
{ {
_response = _requestCreator.Build(_requestMessage, _isQos, _qoSProvider).GetAwaiter().GetResult(); _response = _requestCreator.Build(_requestMessage,
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect)
.GetAwaiter()
.GetResult();
} }
private void ThenTheRequestContainsTheRequestMessage() private void ThenTheRequestContainsTheRequestMessage()
@ -52,5 +65,15 @@
{ {
_response.Data.QosProvider.ShouldBe(_qoSProvider); _response.Data.QosProvider.ShouldBe(_qoSProvider);
} }
private void ThenTheRequestContainsUseCookieContainer()
{
_response.Data.UseCookieContainer.ShouldBe(_useCookieContainer);
}
private void ThenTheRequestContainsAllowAutoRedirect()
{
_response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect);
}
} }
} }

View File

@ -28,7 +28,7 @@
[Fact] [Fact]
public void should_call_scoped_data_repository_correctly() public void should_call_scoped_data_repository_correctly()
{ {
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider()))) this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false)))
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage())) .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
.And(x => x.GivenTheScopedRepoReturns()) .And(x => x.GivenTheScopedRepoReturns())
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())

View File

@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.Responder
[InlineData(OcelotErrorCode.RequestTimedOutError)] [InlineData(OcelotErrorCode.RequestTimedOutError)]
public void should_return_service_unavailable(OcelotErrorCode errorCode) public void should_return_service_unavailable(OcelotErrorCode errorCode)
{ {
ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.ServiceUnavailable); ShouldMapErrorToStatusCode(OcelotErrorCode.RequestTimedOutError, HttpStatusCode.ServiceUnavailable);
} }
@ -120,7 +120,7 @@ namespace Ocelot.UnitTests.Responder
// If this test fails then it's because the number of error codes has changed. // If this test fails then it's because the number of error codes has changed.
// You should make the appropriate changes to the test cases here to ensure // You should make the appropriate changes to the test cases here to ensure
// they cover all the error codes, and then modify this assertion. // they cover all the error codes, and then modify this assertion.
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(30, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?"); Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(31, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
} }
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode) private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)

View File

@ -1,8 +1,6 @@
namespace Ocelot.UnitTests.TestData namespace Ocelot.UnitTests.TestData
{ {
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
public class AuthenticationConfigTestData public class AuthenticationConfigTestData
@ -11,13 +9,6 @@
{ {
yield return new object[] yield return new object[]
{ {
"IdentityServer",
new IdentityServerConfigBuilder()
.WithRequireHttps(true)
.WithApiName("test")
.WithApiSecret("test")
.WithProviderRootUrl("test")
.Build(),
new FileConfiguration new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
@ -30,15 +21,8 @@
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig
{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -51,11 +35,6 @@
yield return new object[] yield return new object[]
{ {
"Jwt",
new JwtConfigBuilder()
.WithAudience("a")
.WithAuthority("au")
.Build(),
new FileConfiguration new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
@ -68,13 +47,8 @@
ReRouteIsCaseSensitive = true, ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer",
JwtConfig = new FileJwtConfig
{
Audience = "a",
Authority = "au"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Cake" version="0.17.0" /> <package id="Cake" version="0.23.0" />
</packages> </packages>