all packages upgraded and tests passing

This commit is contained in:
TomPallister 2020-12-01 11:06:49 +00:00
parent 17b0555f55
commit f62ed72dde
15 changed files with 1999 additions and 1943 deletions

View File

@ -12,19 +12,22 @@ This will bring down everything needed by the admin API.
Providing your own IdentityServer Providing your own IdentityServer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method. All you need to do to hook into your own IdentityServer is add the following to your ConfigureServices method.
.. code-block:: csharp .. code-block:: csharp
public virtual void ConfigureServices(IServiceCollection services) public virtual void ConfigureServices(IServiceCollection services)
{ {
Action<IdentityServerAuthenticationOptions> options = o => { Action<JwtBearerOptions> options = o =>
// o.Authority = ; {
// o.ApiName = ; o.Authority = identityServerRootUrl;
// etc.... o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
}; };
// etc....
};
services services
.AddOcelot() .AddOcelot()

View File

@ -97,16 +97,14 @@ In order to use IdentityServer bearer tokens, register your IdentityServer servi
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
var authenticationProviderKey = "TestKey"; var authenticationProviderKey = "TestKey";
Action<IdentityServerAuthenticationOptions> options = o => Action<JwtBearerOptions> options = o =>
{ {
o.Authority = "https://whereyouridentityserverlives.com"; o.Authority = "https://whereyouridentityserverlives.com";
o.ApiName = "api"; // etc
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
}; };
services.AddAuthentication() services.AddAuthentication()
.AddIdentityServerAuthentication(authenticationProviderKey, options); .AddJwtBearer(authenticationProviderKey, options);
services.AddOcelot(); services.AddOcelot();
} }

View File

@ -31,7 +31,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.1.1" /> <PackageReference Include="IdentityServer4" Version="4.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" /> <PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />

View File

@ -1,7 +1,6 @@
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
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;
@ -10,6 +9,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace Ocelot.Administration namespace Ocelot.Administration
{ {
@ -18,6 +20,7 @@ namespace Ocelot.Administration
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret) public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret)
{ {
var administrationPath = new AdministrationPath(path); var administrationPath = new AdministrationPath(path);
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get); builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
//add identity server for admin area //add identity server for admin area
@ -32,7 +35,7 @@ namespace Ocelot.Administration
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration); return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
} }
public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action<IdentityServerAuthenticationOptions> configureOptions) public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, Action<JwtBearerOptions> configureOptions)
{ {
var administrationPath = new AdministrationPath(path); var administrationPath = new AdministrationPath(path);
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get); builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(IdentityServerMiddlewareConfigurationProvider.Get);
@ -46,11 +49,11 @@ namespace Ocelot.Administration
return new OcelotAdministrationBuilder(builder.Services, builder.Configuration); return new OcelotAdministrationBuilder(builder.Services, builder.Configuration);
} }
private static void AddIdentityServer(Action<IdentityServerAuthenticationOptions> configOptions, IOcelotBuilder builder) private static void AddIdentityServer(Action<JwtBearerOptions> configOptions, IOcelotBuilder builder)
{ {
builder.Services builder.Services
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(configOptions); .AddJwtBearer("Bearer", configOptions);
} }
private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration) private static void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath, IOcelotBuilder builder, IConfiguration configuration)
@ -60,7 +63,9 @@ namespace Ocelot.Administration
.AddIdentityServer(o => .AddIdentityServer(o =>
{ {
o.IssuerUri = "Ocelot"; o.IssuerUri = "Ocelot";
o.EmitStaticAudienceClaim = true;
}) })
.AddInMemoryApiScopes(ApiScopes(identityServerConfiguration))
.AddInMemoryApiResources(Resources(identityServerConfiguration)) .AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration)); .AddInMemoryClients(Client(identityServerConfiguration));
@ -68,14 +73,17 @@ namespace Ocelot.Administration
var baseSchemeUrlAndPort = urlFinder.Find(); var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) builder.Services
.AddIdentityServerAuthentication(o => .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer("Bearer", options =>
{ {
o.Authority = baseSchemeUrlAndPort + adminPath.Path; options.Authority = baseSchemeUrlAndPort + adminPath.Path;
o.ApiName = identityServerConfiguration.ApiName; options.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both; options.TokenValidationParameters = new TokenValidationParameters
o.ApiSecret = identityServerConfiguration.ApiSecret; {
ValidateAudience = false,
};
}); });
//todo - refactor naming.. //todo - refactor naming..
@ -91,6 +99,11 @@ namespace Ocelot.Administration
} }
} }
private static IEnumerable<ApiScope> ApiScopes(IIdentityServerConfiguration identityServerConfiguration)
{
return identityServerConfiguration.AllowedScopes.Select(s => new ApiScope(s));
}
private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration) private static List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
{ {
return new List<ApiResource> return new List<ApiResource>
@ -101,9 +114,9 @@ namespace Ocelot.Administration
{ {
new Secret new Secret
{ {
Value = identityServerConfiguration.ApiSecret.Sha256() Value = identityServerConfiguration.ApiSecret.Sha256(),
} },
} },
}, },
}; };
} }
@ -117,8 +130,8 @@ namespace Ocelot.Administration
ClientId = identityServerConfiguration.ApiName, ClientId = identityServerConfiguration.ApiName,
AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())}, ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
AllowedScopes = { identityServerConfiguration.ApiName } AllowedScopes = identityServerConfiguration.AllowedScopes,
} },
}; };
} }
} }

View File

@ -278,6 +278,11 @@ namespace Ocelot.AcceptanceTests
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddDeveloperSigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiScopes(new List<ApiScope>
{
new ApiScope(apiName, "test"),
new ApiScope(api2Name, "test"),
})
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource
@ -286,12 +291,12 @@ namespace Ocelot.AcceptanceTests
Description = "My API", Description = "My API",
Enabled = true, Enabled = true,
DisplayName = "test", DisplayName = "test",
Scopes = new List<Scope>() Scopes = new List<string>()
{ {
new Scope("api"), "api",
new Scope("api.readOnly"), "api.readOnly",
new Scope("openid"), "openid",
new Scope("offline_access"), "offline_access",
}, },
ApiSecrets = new List<Secret>() ApiSecrets = new List<Secret>()
{ {
@ -311,10 +316,10 @@ namespace Ocelot.AcceptanceTests
Description = "My second API", Description = "My second API",
Enabled = true, Enabled = true,
DisplayName = "second test", DisplayName = "second test",
Scopes = new List<Scope>() Scopes = new List<string>()
{ {
new Scope("api2"), "api2",
new Scope("api2.readOnly"), "api2.readOnly",
}, },
ApiSecrets = new List<Secret>() ApiSecrets = new List<Secret>()
{ {

View File

@ -1,91 +1,91 @@
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Test; using IdentityServer4.Test;
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 Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using System; using System;
using System.Collections.Generic; 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 TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
public class AuthorisationTests : IDisposable public class AuthorisationTests : IDisposable
{ {
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private readonly Steps _steps; private readonly Steps _steps;
private readonly Action<IdentityServerAuthenticationOptions> _options; private readonly Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl; private string _identityServerRootUrl;
private readonly ServiceHandler _serviceHandler; private readonly ServiceHandler _serviceHandler;
public AuthorisationTests() public AuthorisationTests()
{ {
_serviceHandler = new ServiceHandler(); _serviceHandler = new ServiceHandler();
_steps = new Steps(); _steps = new Steps();
var identityServerPort = RandomPortFinder.GetRandomPort(); var identityServerPort = RandomPortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}"; _identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o => _options = o =>
{
o.Authority = _identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
}
[Fact]
public void should_return_response_200_authorising_route()
{
int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> o.Authority = _identityServerRootUrl;
{ o.ApiName = "api";
new FileRoute o.RequireHttpsMetadata = false;
{ o.SupportedTokens = SupportedTokens.Both;
DownstreamPathTemplate = "/", o.ApiSecret = "secret";
DownstreamHostAndPorts = new List<FileHostAndPort> };
{ }
new FileHostAndPort
{ [Fact]
Host = "localhost", public void should_return_response_200_authorising_route()
Port = port, {
} int port = RandomPortFinder.GetRandomPort();
},
DownstreamScheme = "http", var configuration = new FileConfiguration
UpstreamPathTemplate = "/", {
UpstreamHttpMethod = new List<string> { "Get" }, Routes = new List<FileRoute>
AuthenticationOptions = new FileAuthenticationOptions {
{ new FileRoute
AuthenticationProviderKey = "Test" {
}, DownstreamPathTemplate = "/",
AddHeadersToRequest = DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
{"CustomerId", "Claims[CustomerId] > value"}, new FileHostAndPort
{"LocationId", "Claims[LocationId] > value"}, {
{"UserType", "Claims[sub] > value[0] > |"}, Host = "localhost",
{"UserId", "Claims[sub] > value[1] > |"} Port = port,
}, },
AddClaimsToRequest = },
{ DownstreamScheme = "http",
{"CustomerId", "Claims[CustomerId] > value"}, UpstreamPathTemplate = "/",
{"UserType", "Claims[sub] > value[0] > |"}, UpstreamHttpMethod = new List<string> { "Get" },
{"UserId", "Claims[sub] > value[1] > |"} AuthenticationOptions = new FileAuthenticationOptions
}, {
RouteClaimsRequirement = AuthenticationProviderKey = "Test",
{ },
{"UserType", "registered"} AddHeadersToRequest =
} {
} {"CustomerId", "Claims[CustomerId] > value"},
} {"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"},
},
AddClaimsToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"},
},
RouteClaimsRequirement =
{
{"UserType", "registered"},
},
},
},
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
@ -97,54 +97,54 @@ namespace Ocelot.AcceptanceTests
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_403_authorising_route() public void should_return_response_403_authorising_route()
{ {
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test" AuthenticationProviderKey = "Test",
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
{"CustomerId", "Claims[CustomerId] > value"}, {"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"}, {"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"}, {"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"} {"UserId", "Claims[sub] > value[1] > |"},
}, },
AddClaimsToRequest = AddClaimsToRequest =
{ {
{"CustomerId", "Claims[CustomerId] > value"}, {"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"} {"UserId", "Claims[sub] > value[1] > |"},
}, },
RouteClaimsRequirement = RouteClaimsRequirement =
{ {
{"UserType", "registered"} {"UserType", "registered"},
} },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
@ -155,39 +155,39 @@ namespace Ocelot.AcceptanceTests
.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))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_200_using_identity_server_with_allowed_scope() public void should_return_response_200_using_identity_server_with_allowed_scope()
{ {
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
}, },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
@ -198,39 +198,39 @@ namespace Ocelot.AcceptanceTests
.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))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_403_using_identity_server_with_scope_not_allowed() public void should_return_response_403_using_identity_server_with_scope_not_allowed()
{ {
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string>{ "api", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
}, },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt))
@ -241,57 +241,57 @@ namespace Ocelot.AcceptanceTests
.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))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_fix_issue_240() public void should_fix_issue_240()
{ {
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test" AuthenticationProviderKey = "Test",
}, },
RouteClaimsRequirement = RouteClaimsRequirement =
{ {
{"Role", "User"} {"Role", "User"},
} },
} },
} },
}; };
var users = new List<TestUser> var users = new List<TestUser>
{ {
new TestUser new TestUser
{ {
Username = "test", Username = "test",
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("Role", "AdminUser"), new Claim("Role", "AdminUser"),
new Claim("Role", "User") new Claim("Role", "User"),
}, },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, users))
@ -303,170 +303,181 @@ namespace Ocelot.AcceptanceTests
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context => _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{ {
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
} }
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType) private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType)
{ {
_identityServerBuilder = new WebHostBuilder() _identityServerBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(url)
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddDeveloperSigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiScopes(new List<ApiScope>
{ {
new ApiResource new ApiScope(apiName, "test"),
{ new ApiScope("openid", "test"),
Name = apiName, new ApiScope("offline_access", "test"),
Description = "My API", new ApiScope("api.readOnly", "test"),
Enabled = true, })
DisplayName = "test", .AddInMemoryApiResources(new List<ApiResource>
Scopes = new List<Scope>() {
{ new ApiResource
new Scope("api"), {
new Scope("api.readOnly"), Name = apiName,
new Scope("openid"), Description = "My API",
new Scope("offline_access") Enabled = true,
}, DisplayName = "test",
ApiSecrets = new List<Secret>() Scopes = new List<string>()
{ {
new Secret "api",
{ "api.readOnly",
Value = "secret".Sha256() "openid",
} "offline_access",
}, },
UserClaims = new List<string>() ApiSecrets = new List<Secret>()
{ {
"CustomerId", "LocationId", "UserType", "UserId" new Secret
} {
}, Value = "secret".Sha256(),
}) },
.AddInMemoryClients(new List<Client> },
{ UserClaims = new List<string>()
new Client {
{ "CustomerId", "LocationId", "UserType", "UserId",
ClientId = "client", },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, },
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())}, })
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" }, .AddInMemoryClients(new List<Client>
AccessTokenType = tokenType, {
Enabled = true, new Client
RequireClientSecret = false {
} ClientId = "client",
}) AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
.AddTestUsers(new List<TestUser> ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
{ AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
new TestUser AccessTokenType = tokenType,
{ Enabled = true,
Username = "test", RequireClientSecret = false,
Password = "test", },
SubjectId = "registered|1231231", })
Claims = new List<Claim> .AddTestUsers(new List<TestUser>
{ {
new Claim("CustomerId", "123"), new TestUser
new Claim("LocationId", "321") {
} Username = "test",
} Password = "test",
}); SubjectId = "registered|1231231",
}) Claims = new List<Claim>
.Configure(app => {
{ new Claim("CustomerId", "123"),
app.UseIdentityServer(); new Claim("LocationId", "321"),
}) },
.Build(); },
});
_identityServerBuilder.Start(); })
.Configure(app =>
_steps.VerifyIdentiryServerStarted(url); {
} app.UseIdentityServer();
})
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List<TestUser> users) .Build();
{
_identityServerBuilder = new WebHostBuilder() _identityServerBuilder.Start();
.UseUrls(url)
.UseKestrel() _steps.VerifyIdentiryServerStarted(url);
.UseContentRoot(Directory.GetCurrentDirectory()) }
.UseIISIntegration()
.UseUrls(url) private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List<TestUser> users)
.ConfigureServices(services => {
{ _identityServerBuilder = new WebHostBuilder()
services.AddLogging(); .UseUrls(url)
services.AddIdentityServer() .UseKestrel()
.AddDeveloperSigningCredential() .UseContentRoot(Directory.GetCurrentDirectory())
.AddInMemoryApiResources(new List<ApiResource> .UseIISIntegration()
{ .UseUrls(url)
new ApiResource .ConfigureServices(services =>
{ {
Name = apiName, services.AddLogging();
Description = "My API", services.AddIdentityServer()
Enabled = true, .AddDeveloperSigningCredential()
DisplayName = "test", .AddInMemoryApiScopes(new List<ApiScope>
Scopes = new List<Scope>() {
{ new ApiScope(apiName, "test"),
new Scope("api"), })
new Scope("api.readOnly"), .AddInMemoryApiResources(new List<ApiResource>
new Scope("openid"), {
new Scope("offline_access"), new ApiResource
}, {
ApiSecrets = new List<Secret>() Name = apiName,
{ Description = "My API",
new Secret Enabled = true,
{ DisplayName = "test",
Value = "secret".Sha256() Scopes = new List<string>()
} {
}, "api",
UserClaims = new List<string>() "api.readOnly",
{ "openid",
"CustomerId", "LocationId", "UserType", "UserId", "Role" "offline_access",
} },
}, ApiSecrets = new List<Secret>()
}) {
.AddInMemoryClients(new List<Client> new Secret
{ {
new Client Value = "secret".Sha256(),
{ },
ClientId = "client", },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, UserClaims = new List<string>()
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())}, {
AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" }, "CustomerId", "LocationId", "UserType", "UserId", "Role",
AccessTokenType = tokenType, },
Enabled = true, },
RequireClientSecret = false, })
} .AddInMemoryClients(new List<Client>
}) {
.AddTestUsers(users); new Client
}) {
.Configure(app => ClientId = "client",
{ AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
app.UseIdentityServer(); ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
}) AllowedScopes = new List<string> { apiName, "api.readOnly", "openid", "offline_access" },
.Build(); AccessTokenType = tokenType,
Enabled = true,
_identityServerBuilder.Start(); RequireClientSecret = false,
},
_steps.VerifyIdentiryServerStarted(url); })
} .AddTestUsers(users);
})
public void Dispose() .Configure(app =>
{ {
_serviceHandler?.Dispose(); app.UseIdentityServer();
_steps.Dispose(); })
_identityServerBuilder?.Dispose(); .Build();
}
} _identityServerBuilder.Start();
_steps.VerifyIdentiryServerStarted(url);
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}
}
} }

View File

@ -1,210 +1,217 @@
using Xunit; using Xunit;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Test; using IdentityServer4.Test;
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 Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly; using Shouldly;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using TestStack.BDDfy; using TestStack.BDDfy;
public class ClaimsToDownstreamPathTests : IDisposable public class ClaimsToDownstreamPathTests : IDisposable
{ {
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 Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl; private string _identityServerRootUrl;
private string _downstreamFinalPath; private string _downstreamFinalPath;
public ClaimsToDownstreamPathTests() public ClaimsToDownstreamPathTests()
{ {
var identityServerPort = RandomPortFinder.GetRandomPort(); var identityServerPort = RandomPortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}"; _identityServerRootUrl = $"http://localhost:{identityServerPort}";
_steps = new Steps(); _steps = new Steps();
_options = o => _options = o =>
{ {
o.Authority = _identityServerRootUrl; o.Authority = _identityServerRootUrl;
o.ApiName = "api"; o.ApiName = "api";
o.RequireHttpsMetadata = false; o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both; o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret"; o.ApiSecret = "secret";
}; };
} }
[Fact] [Fact]
public void should_return_200_and_change_downstream_path() public void should_return_200_and_change_downstream_path()
{ {
var user = new TestUser() var user = new TestUser()
{ {
Username = "test", Username = "test",
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
}; };
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/users/{userId}", DownstreamPathTemplate = "/users/{userId}",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
}, },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/users", UpstreamPathTemplate = "/users",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api", "openid", "offline_access", "api",
}, },
}, },
ChangeDownstreamPathTemplate = ChangeDownstreamPathTemplate =
{ {
{"userId", "Claims[sub] > value[1] > |"}, {"userId", "Claims[sub] > value[1] > |"},
}, },
}, },
}, },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test")) .And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231"))
.And(x => ThenTheDownstreamPathIs("/users/1231231")) .And(x => ThenTheDownstreamPathIs("/users/1231231"))
.BDDfy(); .BDDfy();
} }
private void ThenTheDownstreamPathIs(string path) private void ThenTheDownstreamPathIs(string path)
{ {
_downstreamFinalPath.ShouldBe(path); _downstreamFinalPath.ShouldBe(path);
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode) private void GivenThereIsAServiceRunningOn(string url, int statusCode)
{ {
_servicebuilder = new WebHostBuilder() _servicebuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(url)
.Configure(app => .Configure(app =>
{ {
app.Run(async context => app.Run(async context =>
{ {
_downstreamFinalPath = context.Request.Path.Value; _downstreamFinalPath = context.Request.Path.Value;
string userId = _downstreamFinalPath.Replace("/users/", string.Empty); string userId = _downstreamFinalPath.Replace("/users/", string.Empty);
var responseBody = $"UserId: {userId}"; var responseBody = $"UserId: {userId}";
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
}) })
.Build(); .Build();
_servicebuilder.Start(); _servicebuilder.Start();
} }
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
{ {
_identityServerBuilder = new WebHostBuilder() _identityServerBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(url)
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddDeveloperSigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiScopes(new List<ApiScope>
{ {
new ApiResource new ApiScope(apiName, "test"),
{ new ApiScope("openid", "test"),
Name = apiName, new ApiScope("offline_access", "test"),
Description = "My API", new ApiScope("api.readOnly", "test"),
Enabled = true, })
DisplayName = "test", .AddInMemoryApiResources(new List<ApiResource>
Scopes = new List<Scope>() {
{ new ApiResource
new Scope("api"), {
new Scope("openid"), Name = apiName,
new Scope("offline_access") Description = "My API",
}, Enabled = true,
ApiSecrets = new List<Secret>() DisplayName = "test",
{ Scopes = new List<string>()
new Secret {
{ "api",
Value = "secret".Sha256() "openid",
} "offline_access",
}, },
UserClaims = new List<string>() ApiSecrets = new List<Secret>()
{ {
"CustomerId", "LocationId", "UserType", "UserId" new Secret
} {
} Value = "secret".Sha256(),
}) },
.AddInMemoryClients(new List<Client> },
{ UserClaims = new List<string>()
new Client {
{ "CustomerId", "LocationId", "UserType", "UserId",
ClientId = "client", },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, },
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())}, })
AllowedScopes = new List<string> { apiName, "openid", "offline_access" }, .AddInMemoryClients(new List<Client>
AccessTokenType = tokenType, {
Enabled = true, new Client
RequireClientSecret = false {
} ClientId = "client",
}) AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
.AddTestUsers(new List<TestUser> ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
{ AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
user AccessTokenType = tokenType,
}); Enabled = true,
}) RequireClientSecret = false,
.Configure(app => },
{ })
app.UseIdentityServer(); .AddTestUsers(new List<TestUser>
}) {
.Build(); user,
});
_identityServerBuilder.Start(); })
.Configure(app =>
_steps.VerifyIdentiryServerStarted(url); {
} app.UseIdentityServer();
})
public void Dispose() .Build();
{
_servicebuilder?.Dispose(); _identityServerBuilder.Start();
_steps.Dispose();
_identityServerBuilder?.Dispose(); _steps.VerifyIdentiryServerStarted(url);
} }
}
} public void Dispose()
{
_servicebuilder?.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}
}
}

View File

@ -1,101 +1,101 @@
using Xunit; using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)] [assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Test; using IdentityServer4.Test;
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 Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Security.Claims; using System.Security.Claims;
using TestStack.BDDfy; using TestStack.BDDfy;
public class ClaimsToHeadersForwardingTests : IDisposable public class ClaimsToHeadersForwardingTests : IDisposable
{ {
private IWebHost _identityServerBuilder; private IWebHost _identityServerBuilder;
private readonly Steps _steps; private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options; private Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl; private string _identityServerRootUrl;
private readonly ServiceHandler _serviceHandler; private readonly ServiceHandler _serviceHandler;
public ClaimsToHeadersForwardingTests() public ClaimsToHeadersForwardingTests()
{ {
_serviceHandler = new ServiceHandler(); _serviceHandler = new ServiceHandler();
_steps = new Steps(); _steps = new Steps();
var identityServerPort = RandomPortFinder.GetRandomPort(); var identityServerPort = RandomPortFinder.GetRandomPort();
_identityServerRootUrl = $"http://localhost:{identityServerPort}"; _identityServerRootUrl = $"http://localhost:{identityServerPort}";
_options = o => _options = o =>
{ {
o.Authority = _identityServerRootUrl; o.Authority = _identityServerRootUrl;
o.ApiName = "api"; o.ApiName = "api";
o.RequireHttpsMetadata = false; o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both; o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret"; o.ApiSecret = "secret";
}; };
} }
[Fact] [Fact]
public void should_return_response_200_and_foward_claim_as_header() public void should_return_response_200_and_foward_claim_as_header()
{ {
var user = new TestUser() var user = new TestUser()
{ {
Username = "test", Username = "test",
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("CustomerId", "123"), new Claim("CustomerId", "123"),
new Claim("LocationId", "1") new Claim("LocationId", "1"),
} },
}; };
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api",
}, },
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
{"CustomerId", "Claims[CustomerId] > value"}, {"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"}, {"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"}, {"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"} {"UserId", "Claims[sub] > value[1] > |"},
} },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
@ -107,98 +107,105 @@ namespace Ocelot.AcceptanceTests
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode) private void GivenThereIsAServiceRunningOn(string url, int statusCode)
{ {
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context => _serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{ {
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First(); var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First(); var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First(); var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First(); var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}"; var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
} }
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
{ {
_identityServerBuilder = new WebHostBuilder() _identityServerBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(url)
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddDeveloperSigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiScopes(new List<ApiScope>
{ {
new ApiResource new ApiScope(apiName, "test"),
{ new ApiScope("openid", "test"),
Name = apiName, new ApiScope("offline_access", "test"),
Description = "My API", new ApiScope("api.readOnly", "test"),
Enabled = true, })
DisplayName = "test", .AddInMemoryApiResources(new List<ApiResource>
Scopes = new List<Scope>() {
{ new ApiResource
new Scope("api"), {
new Scope("openid"), Name = apiName,
new Scope("offline_access") Description = "My API",
}, Enabled = true,
ApiSecrets = new List<Secret>() DisplayName = "test",
{ Scopes = new List<string>()
new Secret {
{ "api",
Value = "secret".Sha256() "openid",
} "offline_access",
}, },
UserClaims = new List<string>() ApiSecrets = new List<Secret>()
{ {
"CustomerId", "LocationId", "UserType", "UserId" new Secret
} {
} Value = "secret".Sha256(),
}) },
.AddInMemoryClients(new List<Client> },
{ UserClaims = new List<string>()
new Client {
{ "CustomerId", "LocationId", "UserType", "UserId",
ClientId = "client", },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, },
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())}, })
AllowedScopes = new List<string> { apiName, "openid", "offline_access" }, .AddInMemoryClients(new List<Client>
AccessTokenType = tokenType, {
Enabled = true, new Client
RequireClientSecret = false {
} ClientId = "client",
}) AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
.AddTestUsers(new List<TestUser> ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
{ AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
user AccessTokenType = tokenType,
}); Enabled = true,
}) RequireClientSecret = false,
.Configure(app => },
{ })
app.UseIdentityServer(); .AddTestUsers(new List<TestUser>
}) {
.Build(); user,
});
_identityServerBuilder.Start(); })
.Configure(app =>
_steps.VerifyIdentiryServerStarted(url); {
} app.UseIdentityServer();
})
public void Dispose() .Build();
{
_serviceHandler?.Dispose(); _identityServerBuilder.Start();
_steps.Dispose();
_identityServerBuilder?.Dispose(); _steps.VerifyIdentiryServerStarted(url);
} }
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}
}
} }

View File

@ -1,23 +1,22 @@
using IdentityServer4.AccessTokenValidation; namespace Ocelot.AcceptanceTests
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{ {
using IdentityServer4.Test; using IdentityServer4.Test;
using Shouldly; using Shouldly;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using TestStack.BDDfy;
using Xunit;
public class ClaimsToQueryStringForwardingTests : IDisposable public class ClaimsToQueryStringForwardingTests : IDisposable
{ {
@ -52,10 +51,10 @@ namespace Ocelot.AcceptanceTests
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("CustomerId", "123"), new Claim("CustomerId", "123"),
new Claim("LocationId", "1") new Claim("LocationId", "1"),
} },
}; };
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
@ -63,38 +62,38 @@ namespace Ocelot.AcceptanceTests
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api",
}, },
}, },
AddQueriesToRequest = AddQueriesToRequest =
{ {
{"CustomerId", "Claims[CustomerId] > value"}, {"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"}, {"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"}, {"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"} {"UserId", "Claims[sub] > value[1] > |"},
} },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
@ -118,10 +117,10 @@ namespace Ocelot.AcceptanceTests
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("CustomerId", "123"), new Claim("CustomerId", "123"),
new Claim("LocationId", "1") new Claim("LocationId", "1"),
} },
}; };
int port = RandomPortFinder.GetRandomPort(); int port = RandomPortFinder.GetRandomPort();
@ -129,38 +128,38 @@ namespace Ocelot.AcceptanceTests
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
Routes = new List<FileRoute> Routes = new List<FileRoute>
{ {
new FileRoute new FileRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = port, Port = port,
} },
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AuthenticationProviderKey = "Test", AuthenticationProviderKey = "Test",
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api",
}, },
}, },
AddQueriesToRequest = AddQueriesToRequest =
{ {
{"CustomerId", "Claims[CustomerId] > value"}, {"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"}, {"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"}, {"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"} {"UserId", "Claims[sub] > value[1] > |"},
} },
} },
} },
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user))
@ -230,6 +229,13 @@ namespace Ocelot.AcceptanceTests
services.AddLogging(); services.AddLogging();
services.AddIdentityServer() services.AddIdentityServer()
.AddDeveloperSigningCredential() .AddDeveloperSigningCredential()
.AddInMemoryApiScopes(new List<ApiScope>
{
new ApiScope(apiName, "test"),
new ApiScope("openid", "test"),
new ApiScope("offline_access", "test"),
new ApiScope("api.readOnly", "test"),
})
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource
@ -238,24 +244,24 @@ namespace Ocelot.AcceptanceTests
Description = "My API", Description = "My API",
Enabled = true, Enabled = true,
DisplayName = "test", DisplayName = "test",
Scopes = new List<Scope>() Scopes = new List<string>()
{ {
new Scope("api"), "api",
new Scope("openid"), "openid",
new Scope("offline_access") "offline_access",
}, },
ApiSecrets = new List<Secret>() ApiSecrets = new List<Secret>()
{ {
new Secret new Secret
{ {
Value = "secret".Sha256() Value = "secret".Sha256(),
} },
}, },
UserClaims = new List<string>() UserClaims = new List<string>()
{ {
"CustomerId", "LocationId", "UserType", "UserId" "CustomerId", "LocationId", "UserType", "UserId",
} },
} },
}) })
.AddInMemoryClients(new List<Client> .AddInMemoryClients(new List<Client>
{ {
@ -267,12 +273,12 @@ namespace Ocelot.AcceptanceTests
AllowedScopes = new List<string> { apiName, "openid", "offline_access" }, AllowedScopes = new List<string> { apiName, "openid", "offline_access" },
AccessTokenType = tokenType, AccessTokenType = tokenType,
Enabled = true, Enabled = true,
RequireClientSecret = false RequireClientSecret = false,
} },
}) })
.AddTestUsers(new List<TestUser> .AddTestUsers(new List<TestUser>
{ {
user user,
}); });
}) })
.Configure(app => .Configure(app =>

View File

@ -64,7 +64,7 @@
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" /> <PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.1.1" /> <PackageReference Include="IdentityServer4" Version="4.1.1" />
<PackageReference Include="Consul" Version="1.6.1.1" /> <PackageReference Include="Consul" Version="1.6.1.1" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" /> <PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="2.0.0-beta-1629" />
<PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" /> <PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />

View File

@ -814,7 +814,7 @@ namespace Ocelot.AcceptanceTests
new KeyValuePair<string, string>("scope", "api"), new KeyValuePair<string, string>("scope", "api"),
new KeyValuePair<string, string>("username", "test"), new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"), new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password") new KeyValuePair<string, string>("grant_type", "password"),
}; };
var content = new FormUrlEncodedContent(formData); var content = new FormUrlEncodedContent(formData);
@ -837,7 +837,7 @@ namespace Ocelot.AcceptanceTests
new KeyValuePair<string, string>("scope", "api.readOnly"), new KeyValuePair<string, string>("scope", "api.readOnly"),
new KeyValuePair<string, string>("username", "test"), new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"), new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password") new KeyValuePair<string, string>("grant_type", "password"),
}; };
var content = new FormUrlEncodedContent(formData); var content = new FormUrlEncodedContent(formData);

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,7 @@
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" /> <PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Microsoft.Data.SQLite" Version="5.0.0" /> <PackageReference Include="Microsoft.Data.SQLite" Version="5.0.0" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.1.1" /> <PackageReference Include="IdentityServer4" Version="4.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" /> <PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />

View File

@ -1,101 +1,102 @@
namespace Ocelot.UnitTests.Administration namespace Ocelot.UnitTests.Administration
{ {
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration;
using Moq; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Administration; using Moq;
using Ocelot.DependencyInjection; using Ocelot.Administration;
using Shouldly; using Ocelot.DependencyInjection;
using System; using Shouldly;
using System.Collections.Generic; using System;
using System.Reflection; using System.Collections.Generic;
using TestStack.BDDfy; using System.Reflection;
using Xunit; using TestStack.BDDfy;
using Xunit;
public class OcelotAdministrationBuilderTests
{ public class OcelotAdministrationBuilderTests
private readonly IServiceCollection _services; {
private IServiceProvider _serviceProvider; private readonly IServiceCollection _services;
private readonly IConfiguration _configRoot; private IServiceProvider _serviceProvider;
private IOcelotBuilder _ocelotBuilder; private readonly IConfiguration _configRoot;
private Exception _ex; private IOcelotBuilder _ocelotBuilder;
private Exception _ex;
public OcelotAdministrationBuilderTests()
{ public OcelotAdministrationBuilderTests()
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>()); {
_services = new ServiceCollection(); _configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
_services.AddSingleton<IWebHostEnvironment>(GetHostingEnvironment()); _services = new ServiceCollection();
_services.AddSingleton(_configRoot); _services.AddSingleton<IWebHostEnvironment>(GetHostingEnvironment());
} _services.AddSingleton(_configRoot);
}
private IWebHostEnvironment GetHostingEnvironment()
{ private IWebHostEnvironment GetHostingEnvironment()
var environment = new Mock<IWebHostEnvironment>(); {
environment var environment = new Mock<IWebHostEnvironment>();
.Setup(e => e.ApplicationName) environment
.Returns(typeof(OcelotAdministrationBuilderTests).GetTypeInfo().Assembly.GetName().Name); .Setup(e => e.ApplicationName)
.Returns(typeof(OcelotAdministrationBuilderTests).GetTypeInfo().Assembly.GetName().Name);
return environment.Object;
} return environment.Object;
}
//keep
[Fact] //keep
public void should_set_up_administration_with_identity_server_options() [Fact]
{ public void should_set_up_administration_with_identity_server_options()
Action<IdentityServerAuthenticationOptions> options = o => { }; {
Action<JwtBearerOptions> options = o => { };
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpAdministration(options)) this.Given(x => WhenISetUpOcelotServices())
.Then(x => ThenAnExceptionIsntThrown()) .When(x => WhenISetUpAdministration(options))
.Then(x => ThenTheCorrectAdminPathIsRegitered()) .Then(x => ThenAnExceptionIsntThrown())
.BDDfy(); .Then(x => ThenTheCorrectAdminPathIsRegitered())
} .BDDfy();
}
//keep
[Fact] //keep
public void should_set_up_administration() [Fact]
{ public void should_set_up_administration()
this.Given(x => WhenISetUpOcelotServices()) {
.When(x => WhenISetUpAdministration()) this.Given(x => WhenISetUpOcelotServices())
.Then(x => ThenAnExceptionIsntThrown()) .When(x => WhenISetUpAdministration())
.Then(x => ThenTheCorrectAdminPathIsRegitered()) .Then(x => ThenAnExceptionIsntThrown())
.BDDfy(); .Then(x => ThenTheCorrectAdminPathIsRegitered())
} .BDDfy();
}
private void WhenISetUpAdministration()
{ private void WhenISetUpAdministration()
_ocelotBuilder.AddAdministration("/administration", "secret"); {
} _ocelotBuilder.AddAdministration("/administration", "secret");
}
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
{ private void WhenISetUpAdministration(Action<JwtBearerOptions> options)
_ocelotBuilder.AddAdministration("/administration", options); {
} _ocelotBuilder.AddAdministration("/administration", options);
}
private void ThenTheCorrectAdminPathIsRegitered()
{ private void ThenTheCorrectAdminPathIsRegitered()
_serviceProvider = _services.BuildServiceProvider(); {
var path = _serviceProvider.GetService<IAdministrationPath>(); _serviceProvider = _services.BuildServiceProvider();
path.Path.ShouldBe("/administration"); var path = _serviceProvider.GetService<IAdministrationPath>();
} path.Path.ShouldBe("/administration");
}
private void WhenISetUpOcelotServices()
{ private void WhenISetUpOcelotServices()
try {
{ try
_ocelotBuilder = _services.AddOcelot(_configRoot); {
} _ocelotBuilder = _services.AddOcelot(_configRoot);
catch (Exception e) }
{ catch (Exception e)
_ex = e; {
} _ex = e;
} }
}
private void ThenAnExceptionIsntThrown()
{ private void ThenAnExceptionIsntThrown()
_ex.ShouldBeNull(); {
} _ex.ShouldBeNull();
} }
} }
}

View File

@ -72,7 +72,7 @@
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" /> <PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4" Version="3.1.1" /> <PackageReference Include="IdentityServer4" Version="4.1.1" />
<PackageReference Include="Steeltoe.Discovery.ClientCore" Version="3.0.1" /> <PackageReference Include="Steeltoe.Discovery.ClientCore" Version="3.0.1" />
<PackageReference Include="Consul" Version="1.6.1.1" /> <PackageReference Include="Consul" Version="1.6.1.1" />
<PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" /> <PackageReference Include="CacheManager.Core" Version="2.0.0-beta-1629" />