Feature/use any id server for admin area (#232)

* initial commits around using any id servers

* add your own id server for admin area

* lots of refactoring, now instead of injecting IWebHostBuilder we just set the Ocelot base url as a configuration extension method..this means people can pass it in on the command line aswell as hardcode which is OK I guess, also can now use your own IdentityServer to authenticate admin area

* updated docs for #231

* some tests that hopefully bump up coverage
This commit is contained in:
Tom Pallister
2018-02-14 18:53:18 +00:00
committed by GitHub
parent 6f177fbf5b
commit 05481f3af3
31 changed files with 876 additions and 546 deletions

View File

@ -8,15 +8,13 @@ namespace Ocelot.Configuration.Creator
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
{
private IBaseUrlFinder _finder;
private Dictionary<string, Func<string>> _placeholders;
private readonly Dictionary<string, Func<string>> _placeholders;
public HeaderFindAndReplaceCreator(IBaseUrlFinder finder)
{
_finder = finder;
_placeholders = new Dictionary<string, Func<string>>();
_placeholders.Add("{BaseUrl}", () => {
return _finder.Find();
});
_placeholders.Add("{BaseUrl}", () => _finder.Find());
}
public HeaderTransformations Create(FileReRoute fileReRoute)

View File

@ -17,4 +17,4 @@ namespace Ocelot.Configuration
public ServiceProviderConfiguration ServiceProviderConfiguration {get;}
public string RequestId {get;}
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Memory;
namespace Ocelot.DependencyInjection
{
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddOcelotBaseUrl(this IConfigurationBuilder builder, string baseUrl)
{
var memorySource = new MemoryConfigurationSource();
memorySource.InitialData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("BaseUrl", baseUrl)
};
builder.Add(memorySource);
return builder;
}
}
}

View File

@ -2,6 +2,7 @@ using Butterfly.Client.AspNetCore;
using CacheManager.Core;
using System;
using System.Net.Http;
using IdentityServer4.AccessTokenValidation;
namespace Ocelot.DependencyInjection
{
@ -9,8 +10,9 @@ namespace Ocelot.DependencyInjection
{
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions);
IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler);
}
}

View File

@ -1,66 +1,68 @@
using CacheManager.Core;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.Configuration.Validator;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.RateLimit;
using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.ServiceDiscovery;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
using System.Net.Http;
using Butterfly.Client.AspNetCore;
using Microsoft.Extensions.Options;
namespace Ocelot.DependencyInjection
{
using CacheManager.Core;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
using Ocelot.Configuration.Provider;
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.Configuration.Validator;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.RateLimit;
using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.ServiceDiscovery;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
using System.Net.Http;
using Butterfly.Client.AspNetCore;
public class OcelotBuilder : IOcelotBuilder
{
private readonly IServiceCollection _services;
private readonly IConfiguration _configurationRoot;
private IDelegatingHandlerHandlerProvider _provider;
private readonly IDelegatingHandlerHandlerProvider _provider;
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
{
_configurationRoot = configurationRoot;
_services = services;
//add default cache settings...
Action<ConfigurationBuilderCachePart> defaultCachingSettings = x =>
{
@ -109,7 +111,7 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder>();
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
@ -166,6 +168,21 @@ namespace Ocelot.DependencyInjection
return new OcelotAdministrationBuilder(_services, _configurationRoot);
}
public IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configureOptions)
{
var administrationPath = new AdministrationPath(path);
if (configureOptions != null)
{
AddIdentityServer(configureOptions);
}
//todo - hack because we add this earlier so it always exists for some reason...investigate..
var descriptor = new ServiceDescriptor(typeof(IAdministrationPath), administrationPath);
_services.Replace(descriptor);
return new OcelotAdministrationBuilder(_services, _configurationRoot);
}
public IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler)
{
_provider.Add(delegatingHandler);
@ -221,6 +238,13 @@ namespace Ocelot.DependencyInjection
return this;
}
private void AddIdentityServer(Action<IdentityServerAuthenticationOptions> configOptions)
{
_services
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(configOptions);
}
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
{
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
@ -232,11 +256,10 @@ namespace Ocelot.DependencyInjection
.AddInMemoryApiResources(Resources(identityServerConfiguration))
.AddInMemoryClients(Client(identityServerConfiguration));
//todo - refactor a method so we know why this is happening
var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder));
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
var urlFinder = new BaseUrlFinder(_configurationRoot);
var baseSchemeUrlAndPort = urlFinder.Find();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
_services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(o =>

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.Configuration;
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
namespace Ocelot.DependencyInjection

View File

@ -21,7 +21,6 @@ namespace Ocelot.Errors.Middleware
private readonly IRequestScopedDataRepository _requestScopedDataRepository;
private readonly IOcelotConfigurationProvider _configProvider;
public ExceptionHandlerMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository requestScopedDataRepository,

View File

@ -1,21 +1,21 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace Ocelot.Middleware
{
public class BaseUrlFinder : IBaseUrlFinder
{
private readonly IWebHostBuilder _webHostBuilder;
private readonly IConfiguration _config;
public BaseUrlFinder(IWebHostBuilder webHostBuilder)
public BaseUrlFinder(IConfiguration config)
{
_webHostBuilder = webHostBuilder;
_config = config;
}
public string Find()
{
var baseSchemeUrlAndPort = _webHostBuilder.GetSetting(WebHostDefaults.ServerUrlsKey);
var baseUrl = _config.GetValue("BaseUrl", "");
return string.IsNullOrEmpty(baseSchemeUrlAndPort) ? "http://localhost:5000" : baseSchemeUrlAndPort;
return string.IsNullOrEmpty(baseUrl) ? "http://localhost:5000" : baseUrl;
}
}
}

View File

@ -1,36 +1,14 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Authentication.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Claims.Middleware;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
using Ocelot.RequestId.Middleware;
using Ocelot.Responder.Middleware;
using Ocelot.RateLimit.Middleware;
namespace Ocelot.Middleware
namespace Ocelot.Middleware
{
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Authorisation.Middleware;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
@ -38,8 +16,21 @@ namespace Ocelot.Middleware
using Ocelot.Configuration.Repository;
using Ocelot.Configuration.Setter;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Raft;
using Ocelot.Responses;
using Ocelot.Authentication.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Claims.Middleware;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
using Ocelot.RequestId.Middleware;
using Ocelot.Responder.Middleware;
using Ocelot.RateLimit.Middleware;
using Rafty.Concensus;
using Rafty.Infrastructure;
@ -67,7 +58,7 @@ namespace Ocelot.Middleware
{
var configuration = await CreateConfiguration(builder);
await CreateAdministrationArea(builder, configuration);
CreateAdministrationArea(builder, configuration);
if(UsingRafty(builder))
{
@ -290,15 +281,19 @@ namespace Ocelot.Middleware
return new OkResponse();
}
private static async Task CreateAdministrationArea(IApplicationBuilder builder, IOcelotConfiguration configuration)
private static void CreateAdministrationArea(IApplicationBuilder builder, IOcelotConfiguration configuration)
{
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)
if(!string.IsNullOrEmpty(configuration.AdministrationPath))
{
builder.Map(configuration.AdministrationPath, app =>
{
app.UseIdentityServer();
//todo - hack so we know that we are using internal identity server
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
if (identityServerConfiguration != null)
{
app.UseIdentityServer();
}
app.UseAuthentication();
app.UseMvc();
});

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;
using Ocelot.Configuration;
using Ocelot.Configuration.Provider;
using Ocelot.Middleware;
using Rafty.Concensus;
using Rafty.Infrastructure;
@ -15,15 +16,15 @@ namespace Ocelot.Raft
{
private readonly IOptions<FilePeers> _options;
private List<IPeer> _peers;
private IWebHostBuilder _builder;
private IBaseUrlFinder _finder;
private IOcelotConfigurationProvider _provider;
private IIdentityServerConfiguration _identityServerConfig;
public FilePeersProvider(IOptions<FilePeers> options, IWebHostBuilder builder, IOcelotConfigurationProvider provider, IIdentityServerConfiguration identityServerConfig)
public FilePeersProvider(IOptions<FilePeers> options, IBaseUrlFinder finder, IOcelotConfigurationProvider provider, IIdentityServerConfiguration identityServerConfig)
{
_identityServerConfig = identityServerConfig;
_provider = provider;
_builder = builder;
_finder = finder;
_options = options;
_peers = new List<IPeer>();
//todo - sort out async nonsense..
@ -32,7 +33,7 @@ namespace Ocelot.Raft
{
var httpClient = new HttpClient();
//todo what if this errors?
var httpPeer = new HttpPeer(item.HostAndPort, httpClient, _builder, config.Data, _identityServerConfig);
var httpPeer = new HttpPeer(item.HostAndPort, httpClient, _finder, config.Data, _identityServerConfig);
_peers.Add(httpPeer);
}
}

View File

@ -7,6 +7,7 @@ using Newtonsoft.Json;
using Ocelot.Authentication;
using Ocelot.Configuration;
using Ocelot.Configuration.Provider;
using Ocelot.Middleware;
using Rafty.Concensus;
using Rafty.FiniteStateMachine;
@ -23,7 +24,7 @@ namespace Ocelot.Raft
private IOcelotConfiguration _config;
private IIdentityServerConfiguration _identityServerConfiguration;
public HttpPeer(string hostAndPort, HttpClient httpClient, IWebHostBuilder builder, IOcelotConfiguration config, IIdentityServerConfiguration identityServerConfiguration)
public HttpPeer(string hostAndPort, HttpClient httpClient, IBaseUrlFinder finder, IOcelotConfiguration config, IIdentityServerConfiguration identityServerConfiguration)
{
_identityServerConfiguration = identityServerConfiguration;
_config = config;
@ -33,7 +34,7 @@ namespace Ocelot.Raft
_jsonSerializerSettings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All
};
_baseSchemeUrlAndPort = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
_baseSchemeUrlAndPort = finder.Find();
}
public string Id {get; private set;}

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.Raft;
using Rafty.Concensus;
using Rafty.FiniteStateMachine;
@ -23,12 +24,12 @@ namespace Ocelot.Raft
private string _baseSchemeUrlAndPort;
private JsonSerializerSettings _jsonSerialiserSettings;
public RaftController(INode node, IOcelotLoggerFactory loggerFactory, IWebHostBuilder builder)
public RaftController(INode node, IOcelotLoggerFactory loggerFactory, IBaseUrlFinder finder)
{
_jsonSerialiserSettings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All
};
_baseSchemeUrlAndPort = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
_baseSchemeUrlAndPort = finder.Find();
_logger = loggerFactory.CreateLogger<RaftController>();
_node = node;
}
@ -81,4 +82,4 @@ namespace Ocelot.Raft
}
}
}
}
}