mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
got identity server access token acceptance test working, created factory for choosing auth handlers, a creator for making the auth handlers, some general refactoring...next step is injecting the config for the auth handler creator in some way or just passing it in
This commit is contained in:
parent
34bac7e0d4
commit
320b442526
@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
public class AuthenticationHandler
|
||||||
|
{
|
||||||
|
public AuthenticationHandler(string provider, RequestDelegate handler)
|
||||||
|
{
|
||||||
|
Provider = provider;
|
||||||
|
Handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Provider { get; private set; }
|
||||||
|
public RequestDelegate Handler { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cannot unit test things in this class due to use of extension methods
|
||||||
|
/// </summary>
|
||||||
|
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
|
||||||
|
{
|
||||||
|
public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var builder = app.New();
|
||||||
|
|
||||||
|
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
|
||||||
|
{
|
||||||
|
//todo sort these options out
|
||||||
|
Authority = "http://localhost:51888",
|
||||||
|
ScopeName = "api",
|
||||||
|
|
||||||
|
RequireHttpsMetadata = false
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.UseMvc();
|
||||||
|
|
||||||
|
var authenticationNext = builder.Build();
|
||||||
|
|
||||||
|
return new OkResponse<RequestDelegate>(authenticationNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Library.Infrastructure.Errors;
|
||||||
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
public class AuthenticationProviderFactory : IAuthenticationProviderFactory
|
||||||
|
{
|
||||||
|
private readonly IAuthenticationHandlerCreator _creator;
|
||||||
|
|
||||||
|
public AuthenticationProviderFactory(IAuthenticationHandlerCreator creator)
|
||||||
|
{
|
||||||
|
_creator = creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<AuthenticationHandler> Get(string provider, IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var handler = _creator.CreateIdentityServerAuthenticationHandler(app);
|
||||||
|
|
||||||
|
if (!handler.IsError)
|
||||||
|
{
|
||||||
|
return new OkResponse<AuthenticationHandler>(new AuthenticationHandler(provider, handler.Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorResponse<AuthenticationHandler>(new List<Error>
|
||||||
|
{
|
||||||
|
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {provider}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
public interface IAuthenticationHandlerCreator
|
||||||
|
{
|
||||||
|
Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
public interface IAuthenticationProviderFactory
|
||||||
|
{
|
||||||
|
Response<AuthenticationHandler> Get(string provider, IApplicationBuilder app);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using Ocelot.Library.Infrastructure.Errors;
|
||||||
|
|
||||||
|
namespace Ocelot.Library.Infrastructure.Authentication
|
||||||
|
{
|
||||||
|
public class UnableToCreateAuthenticationHandlerError : Error
|
||||||
|
{
|
||||||
|
public UnableToCreateAuthenticationHandlerError(string message)
|
||||||
|
: base(message, OcelotErrorCode.UnableToCreateAuthenticationHandlerError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
UnableToFindDownstreamRouteError,
|
UnableToFindDownstreamRouteError,
|
||||||
CannotAddDataError,
|
CannotAddDataError,
|
||||||
CannotFindDataError,
|
CannotFindDataError,
|
||||||
UnableToCompleteRequestError
|
UnableToCompleteRequestError,
|
||||||
|
UnableToCreateAuthenticationHandlerError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -7,6 +8,11 @@ using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
|||||||
using Ocelot.Library.Infrastructure.Errors;
|
using Ocelot.Library.Infrastructure.Errors;
|
||||||
using Ocelot.Library.Infrastructure.Repository;
|
using Ocelot.Library.Infrastructure.Repository;
|
||||||
using Ocelot.Library.Infrastructure.Responses;
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ocelot.Library.Infrastructure.Authentication;
|
||||||
|
|
||||||
namespace Ocelot.Library.Infrastructure.Middleware
|
namespace Ocelot.Library.Infrastructure.Middleware
|
||||||
{
|
{
|
||||||
@ -16,13 +22,15 @@ namespace Ocelot.Library.Infrastructure.Middleware
|
|||||||
private RequestDelegate _authenticationNext;
|
private RequestDelegate _authenticationNext;
|
||||||
private readonly IScopedRequestDataRepository _scopedRequestDataRepository;
|
private readonly IScopedRequestDataRepository _scopedRequestDataRepository;
|
||||||
private readonly IApplicationBuilder _app;
|
private readonly IApplicationBuilder _app;
|
||||||
|
private readonly IAuthenticationProviderFactory _authProviderFactory;
|
||||||
|
|
||||||
public AuthenticationMiddleware(RequestDelegate next, IApplicationBuilder app,
|
public AuthenticationMiddleware(RequestDelegate next, IApplicationBuilder app,
|
||||||
IScopedRequestDataRepository scopedRequestDataRepository)
|
IScopedRequestDataRepository scopedRequestDataRepository, IAuthenticationProviderFactory authProviderFactory)
|
||||||
: base(scopedRequestDataRepository)
|
: base(scopedRequestDataRepository)
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_scopedRequestDataRepository = scopedRequestDataRepository;
|
_scopedRequestDataRepository = scopedRequestDataRepository;
|
||||||
|
_authProviderFactory = authProviderFactory;
|
||||||
_app = app;
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,25 +46,17 @@ namespace Ocelot.Library.Infrastructure.Middleware
|
|||||||
|
|
||||||
if (IsAuthenticatedRoute(downstreamRoute.Data.ReRoute))
|
if (IsAuthenticatedRoute(downstreamRoute.Data.ReRoute))
|
||||||
{
|
{
|
||||||
//todo - build auth pipeline and then call normal pipeline if all good?
|
var authenticationNext = _authProviderFactory.Get(downstreamRoute.Data.ReRoute.AuthenticationProvider, _app);
|
||||||
//create new app builder
|
|
||||||
var builder = _app.New();
|
if (!authenticationNext.IsError)
|
||||||
//set up any options for the authentication
|
|
||||||
var jwtBearerOptions = new JwtBearerOptions
|
|
||||||
{
|
{
|
||||||
AutomaticAuthenticate = true,
|
await authenticationNext.Data.Handler.Invoke(context);
|
||||||
AutomaticChallenge = true,
|
}
|
||||||
RequireHttpsMetadata = false,
|
else
|
||||||
};
|
{
|
||||||
//set the authentication middleware
|
SetPipelineError(authenticationNext.Errors);
|
||||||
builder.UseJwtBearerAuthentication(jwtBearerOptions);
|
}
|
||||||
//use mvc so we hit the catch all authorised controller
|
|
||||||
builder.UseMvc();
|
|
||||||
//then build it
|
|
||||||
_authenticationNext = builder.Build();
|
|
||||||
//then call it
|
|
||||||
await _authenticationNext(context);
|
|
||||||
//check if the user is authenticated
|
|
||||||
if (context.User.Identity.IsAuthenticated)
|
if (context.User.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
await _next.Invoke(context);
|
await _next.Invoke(context);
|
||||||
|
@ -43,7 +43,6 @@ namespace Ocelot.Library.Infrastructure.Middleware
|
|||||||
{
|
{
|
||||||
await _responder.CreateErrorResponse(context, 500);
|
await _responder.CreateErrorResponse(context, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,10 @@ using Microsoft.AspNetCore.Http;
|
|||||||
|
|
||||||
namespace Ocelot.Library.Infrastructure.Responder
|
namespace Ocelot.Library.Infrastructure.Responder
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cannot unit test things in this class due to methods not being implemented
|
||||||
|
/// on .net concretes used for testing
|
||||||
|
/// </summary>
|
||||||
public class HttpContextResponder : IHttpResponder
|
public class HttpContextResponder : IHttpResponder
|
||||||
{
|
{
|
||||||
public async Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response)
|
public async Task<HttpContext> CreateResponse(HttpContext context, HttpResponseMessage response)
|
||||||
|
@ -1,34 +1,35 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0-*",
|
"version": "1.0.0-*",
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.NETCore.App": {
|
"Microsoft.NETCore.App": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "platform"
|
"type": "platform"
|
||||||
|
},
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.0.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.0.0",
|
||||||
|
"System.Text.RegularExpressions": "4.1.0",
|
||||||
|
"YamlDotNet": "3.9.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OAuth": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Google": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Facebook": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Twitter": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.0.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication": "1.0.0",
|
||||||
|
"IdentityServer4.AccessTokenValidation": "1.0.1-rc2"
|
||||||
},
|
},
|
||||||
"Microsoft.AspNetCore.Mvc": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Configuration.Json": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Logging": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Logging.Console": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Logging.Debug": "1.0.0",
|
|
||||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Http": "1.0.0",
|
|
||||||
"System.Text.RegularExpressions": "4.1.0",
|
|
||||||
"YamlDotNet": "3.9.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.OAuth": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.Google": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.Facebook": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.Twitter": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.0.0",
|
|
||||||
"Microsoft.AspNetCore.Authentication": "1.0.0"
|
|
||||||
},
|
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netcoreapp1.4": {
|
"netcoreapp1.4": {
|
||||||
|
@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration.Memory;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ocelot.Library.Infrastructure.Authentication;
|
||||||
using Ocelot.Library.Infrastructure.Configuration;
|
using Ocelot.Library.Infrastructure.Configuration;
|
||||||
using Ocelot.Library.Infrastructure.Configuration.Yaml;
|
using Ocelot.Library.Infrastructure.Configuration.Yaml;
|
||||||
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
||||||
@ -41,6 +42,7 @@ namespace Ocelot
|
|||||||
{
|
{
|
||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
|
services.AddMvcCore().AddAuthorization().AddJsonFormatters();
|
||||||
|
|
||||||
services.Configure<YamlConfiguration>(Configuration);
|
services.Configure<YamlConfiguration>(Configuration);
|
||||||
services.AddAuthentication();
|
services.AddAuthentication();
|
||||||
@ -55,6 +57,8 @@ namespace Ocelot
|
|||||||
services.AddSingleton<IHttpResponder, HttpContextResponder>();
|
services.AddSingleton<IHttpResponder, HttpContextResponder>();
|
||||||
services.AddSingleton<IRequestBuilder, HttpRequestBuilder>();
|
services.AddSingleton<IRequestBuilder, HttpRequestBuilder>();
|
||||||
services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
||||||
|
services.AddSingleton<IAuthenticationProviderFactory, AuthenticationProviderFactory>();
|
||||||
|
services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
|
||||||
|
|
||||||
// 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
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using IdentityServer4.Services.InMemory;
|
using IdentityServer4.Services.InMemory;
|
||||||
@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Ocelot.Library.Infrastructure.Configuration.Yaml;
|
using Ocelot.Library.Infrastructure.Configuration.Yaml;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -21,17 +23,17 @@ namespace Ocelot.AcceptanceTests
|
|||||||
{
|
{
|
||||||
public class AuthenticationTests : IDisposable
|
public class AuthenticationTests : IDisposable
|
||||||
{
|
{
|
||||||
private TestServer _server;
|
private TestServer _ocelotServer;
|
||||||
private HttpClient _client;
|
private HttpClient _ocelotClient;
|
||||||
private HttpResponseMessage _response;
|
private HttpResponseMessage _response;
|
||||||
private readonly string _configurationPath;
|
private readonly string _configurationPath;
|
||||||
private StringContent _postContent;
|
private StringContent _postContent;
|
||||||
private IWebHost _builder;
|
private IWebHost _ocelotBbuilder;
|
||||||
|
|
||||||
// Sadly we need to change this when we update the netcoreapp version to make the test update the config correctly
|
// Sadly we need to change this when we update the netcoreapp version to make the test update the config correctly
|
||||||
private double _netCoreAppVersion = 1.4;
|
private double _netCoreAppVersion = 1.4;
|
||||||
private HttpClient _idServerClient;
|
private BearerToken _token;
|
||||||
private TestServer _idServer;
|
private IWebHost _identityServerBuilder;
|
||||||
|
|
||||||
public AuthenticationTests()
|
public AuthenticationTests()
|
||||||
{
|
{
|
||||||
@ -39,7 +41,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_401_using_jwt()
|
public void should_return_401_using_identity_server_access_token()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888"))
|
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888"))
|
||||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
|
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
|
||||||
@ -63,6 +65,33 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_201_using_identity_server_access_token()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888"))
|
||||||
|
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
|
||||||
|
.And(x => x.GivenIHaveAToken("http://localhost:51888"))
|
||||||
|
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<YamlReRoute>
|
||||||
|
{
|
||||||
|
new YamlReRoute
|
||||||
|
{
|
||||||
|
DownstreamTemplate = "http://localhost:51876/",
|
||||||
|
UpstreamTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "Post",
|
||||||
|
Authentication = "JwtBearerAuthentication"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.And(x => x.GivenTheApiGatewayIsRunning())
|
||||||
|
.And(x => x.GivenIHaveAddedATokenToMyRequest())
|
||||||
|
.And(x => x.GivenThePostHasContent("postContent"))
|
||||||
|
.When(x => x.WhenIPostUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenThePostHasContent(string postcontent)
|
private void GivenThePostHasContent(string postcontent)
|
||||||
{
|
{
|
||||||
_postContent = new StringContent(postcontent);
|
_postContent = new StringContent(postcontent);
|
||||||
@ -73,10 +102,10 @@ namespace Ocelot.AcceptanceTests
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void GivenTheApiGatewayIsRunning()
|
private void GivenTheApiGatewayIsRunning()
|
||||||
{
|
{
|
||||||
_server = new TestServer(new WebHostBuilder()
|
_ocelotServer = new TestServer(new WebHostBuilder()
|
||||||
.UseStartup<Startup>());
|
.UseStartup<Startup>());
|
||||||
|
|
||||||
_client = _server.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
|
private void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
|
||||||
@ -96,7 +125,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_builder = new WebHostBuilder()
|
_ocelotBbuilder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
@ -112,12 +141,12 @@ namespace Ocelot.AcceptanceTests
|
|||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_builder.Start();
|
_ocelotBbuilder.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAnIdentityServerOn(string url)
|
private void GivenThereIsAnIdentityServerOn(string url)
|
||||||
{
|
{
|
||||||
var builder = new WebHostBuilder()
|
_identityServerBuilder = new WebHostBuilder()
|
||||||
.UseUrls(url)
|
.UseUrls(url)
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
@ -126,41 +155,51 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddDeveloperIdentityServer()
|
services.AddDeveloperIdentityServer()
|
||||||
.AddInMemoryClients(new List<Client> {
|
|
||||||
new Client
|
|
||||||
{
|
|
||||||
ClientId = "test",
|
|
||||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
|
||||||
ClientSecrets = new List<Secret> { new Secret("test".Sha256()) },
|
|
||||||
AllowedScopes = new List<string> { "api1" },
|
|
||||||
AllowAccessToAllScopes = true,
|
|
||||||
AccessTokenType = AccessTokenType.Jwt,
|
|
||||||
Enabled = true
|
|
||||||
|
|
||||||
} })
|
|
||||||
.AddInMemoryScopes(new List<Scope> { new Scope
|
.AddInMemoryScopes(new List<Scope> { new Scope
|
||||||
{
|
{
|
||||||
Name = "api1",
|
Name = "api",
|
||||||
Description = "My API",
|
Description = "My API",
|
||||||
Enabled = true
|
Enabled = true
|
||||||
|
|
||||||
}})
|
}})
|
||||||
|
.AddInMemoryClients(new List<Client> {
|
||||||
|
new Client
|
||||||
|
{
|
||||||
|
ClientId = "client",
|
||||||
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
|
ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) },
|
||||||
|
AllowedScopes = new List<string> { "api" },
|
||||||
|
AccessTokenType = AccessTokenType.Jwt,
|
||||||
|
Enabled = true,
|
||||||
|
RequireClientSecret = false
|
||||||
|
} })
|
||||||
.AddInMemoryUsers(new List<InMemoryUser> { new InMemoryUser
|
.AddInMemoryUsers(new List<InMemoryUser> { new InMemoryUser
|
||||||
{
|
{
|
||||||
Username = "test", Password = "test", Enabled = true, Subject = "asdads"
|
Username = "test",
|
||||||
|
Password = "test",
|
||||||
|
Enabled = true,
|
||||||
|
Subject = "asdads"
|
||||||
}});
|
}});
|
||||||
})
|
})
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
app.UseIdentityServer();
|
app.UseIdentityServer();
|
||||||
});
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
_idServer = new TestServer(builder);
|
_identityServerBuilder.Start();
|
||||||
_idServerClient = _idServer.CreateClient();
|
|
||||||
|
|
||||||
var response = _idServerClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
|
VerifyIdentiryServerStarted(url);
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
var content = response.Content.ReadAsStringAsync().Result;
|
}
|
||||||
|
|
||||||
|
private void VerifyIdentiryServerStarted(string url)
|
||||||
|
{
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenIHaveAToken(string url)
|
private void GivenIHaveAToken(string url)
|
||||||
@ -168,21 +207,32 @@ namespace Ocelot.AcceptanceTests
|
|||||||
var tokenUrl = $"{url}/connect/token";
|
var tokenUrl = $"{url}/connect/token";
|
||||||
var formData = new List<KeyValuePair<string, string>>
|
var formData = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("client_id", "test"),
|
new KeyValuePair<string, string>("client_id", "client"),
|
||||||
new KeyValuePair<string, string>("client_secret", "test".Sha256()),
|
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||||
new KeyValuePair<string, string>("scope", "api1"),
|
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);
|
||||||
var response = _idServerClient.PostAsync(tokenUrl, content).Result;
|
|
||||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = httpClient.PostAsync(tokenUrl, content).Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenIHaveAddedATokenToMyRequest()
|
||||||
|
{
|
||||||
|
_ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIPostUrlOnTheApiGateway(string url)
|
private void WhenIPostUrlOnTheApiGateway(string url)
|
||||||
{
|
{
|
||||||
_response = _client.PostAsync(url, _postContent).Result;
|
_response = _ocelotClient.PostAsync(url, _postContent).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
||||||
@ -192,11 +242,22 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_idServerClient?.Dispose();
|
_ocelotBbuilder?.Dispose();
|
||||||
_idServer?.Dispose();
|
_ocelotClient?.Dispose();
|
||||||
_builder?.Dispose();
|
_ocelotServer?.Dispose();
|
||||||
_client.Dispose();
|
_identityServerBuilder?.Dispose();
|
||||||
_server.Dispose();
|
}
|
||||||
|
|
||||||
|
class BearerToken
|
||||||
|
{
|
||||||
|
[JsonProperty("access_token")]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("expires_in")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("token_type")]
|
||||||
|
public string TokenType { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,4 @@ ReRoutes:
|
|||||||
- DownstreamTemplate: http://localhost:51879/
|
- DownstreamTemplate: http://localhost:51879/
|
||||||
UpstreamTemplate: /
|
UpstreamTemplate: /
|
||||||
UpstreamHttpMethod: Get
|
UpstreamHttpMethod: Get
|
||||||
Authentication: IdentityServer
|
Authentication: IdentityServer.AccessToken
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Library.Infrastructure.Authentication;
|
||||||
|
using Ocelot.Library.Infrastructure.Errors;
|
||||||
|
using Ocelot.Library.Infrastructure.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Authentication
|
||||||
|
{
|
||||||
|
public class AuthenticationProviderFactoryTests
|
||||||
|
{
|
||||||
|
private readonly IAuthenticationProviderFactory _authenticationProviderFactory;
|
||||||
|
private readonly Mock<IApplicationBuilder> _app;
|
||||||
|
private readonly Mock<IAuthenticationHandlerCreator> _creator;
|
||||||
|
|
||||||
|
private string _provider;
|
||||||
|
private Response<AuthenticationHandler> _result;
|
||||||
|
|
||||||
|
public AuthenticationProviderFactoryTests()
|
||||||
|
{
|
||||||
|
_app = new Mock<IApplicationBuilder>();
|
||||||
|
_creator = new Mock<IAuthenticationHandlerCreator>();
|
||||||
|
_authenticationProviderFactory = new AuthenticationProviderFactory(_creator.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_identity_server_access_token_provider()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken"))
|
||||||
|
.And(x => x.GivenTheCreatorReturns())
|
||||||
|
.When(x => x.WhenIGetFromTheFactory())
|
||||||
|
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer.AccessToken"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_if_cannot_create_handler()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken"))
|
||||||
|
.And(x => x.GivenTheCreatorReturnsAnError())
|
||||||
|
.When(x => x.WhenIGetFromTheFactory())
|
||||||
|
.Then(x => x.ThenAnErrorResponseIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheCreatorReturnsAnError()
|
||||||
|
{
|
||||||
|
_creator
|
||||||
|
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>()))
|
||||||
|
.Returns(new ErrorResponse<RequestDelegate>(new List<Error>
|
||||||
|
{
|
||||||
|
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheCreatorReturns()
|
||||||
|
{
|
||||||
|
_creator
|
||||||
|
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>()))
|
||||||
|
.Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheProviderIs(string provider)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetFromTheFactory()
|
||||||
|
{
|
||||||
|
_result = _authenticationProviderFactory.Get(_provider, _app.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHandlerIsReturned(string expected)
|
||||||
|
{
|
||||||
|
_result.Data.Provider.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnErrorResponseIsReturned()
|
||||||
|
{
|
||||||
|
_result.IsError.ShouldBeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using Ocelot.Library.Infrastructure.Authentication;
|
||||||
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
|
||||||
using Ocelot.Library.Infrastructure.Middleware;
|
using Ocelot.Library.Infrastructure.Middleware;
|
||||||
using Ocelot.Library.Infrastructure.Repository;
|
using Ocelot.Library.Infrastructure.Repository;
|
||||||
@ -21,6 +22,7 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
public class AuthenticationMiddlewareTests : IDisposable
|
public class AuthenticationMiddlewareTests : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Mock<IScopedRequestDataRepository> _scopedRepository;
|
private readonly Mock<IScopedRequestDataRepository> _scopedRepository;
|
||||||
|
private readonly Mock<IAuthenticationProviderFactory> _authFactory;
|
||||||
private readonly string _url;
|
private readonly string _url;
|
||||||
private readonly TestServer _server;
|
private readonly TestServer _server;
|
||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
@ -31,10 +33,11 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
{
|
{
|
||||||
_url = "http://localhost:51879";
|
_url = "http://localhost:51879";
|
||||||
_scopedRepository = new Mock<IScopedRequestDataRepository>();
|
_scopedRepository = new Mock<IScopedRequestDataRepository>();
|
||||||
|
_authFactory = new Mock<IAuthenticationProviderFactory>();
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.ConfigureServices(x =>
|
.ConfigureServices(x =>
|
||||||
{
|
{
|
||||||
|
x.AddSingleton(_authFactory.Object);
|
||||||
x.AddSingleton(_scopedRepository.Object);
|
x.AddSingleton(_scopedRepository.Object);
|
||||||
})
|
})
|
||||||
.UseUrls(_url)
|
.UseUrls(_url)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user