test passing with authentication being provided by the user and mapped to the re route in config

This commit is contained in:
Tom Pallister
2017-11-01 15:25:55 +00:00
parent 967f0f7128
commit 3f2af85969
32 changed files with 280 additions and 606 deletions

View File

@ -1,4 +1,5 @@
using System;
/*
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Configuration;
@ -57,3 +58,4 @@ namespace Ocelot.Authentication.JsonConverters
}
*/

View File

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

View File

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

View File

@ -1,38 +1,12 @@
using System.Collections.Generic;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator
{
public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
{
private readonly IAuthenticationProviderConfigCreator _creator;
public AuthenticationOptionsCreator(IAuthenticationProviderConfigCreator creator)
public AuthenticationOptions Create(FileReRoute reRoute)
{
_creator = creator;
}
public AuthenticationOptions Create(FileReRoute reRoute, List<FileAuthenticationOptions> authOptions)
{
//todo - loop is crap..
foreach(var authOption in authOptions)
{
if(reRoute.AuthenticationProviderKey == authOption.AuthenticationProviderKey)
{
var authenticationConfig = _creator.Create(authOption);
return new AuthenticationOptionsBuilder()
.WithProvider(authOption.Provider)
.WithAllowedScopes(authOption.AllowedScopes)
.WithConfig(authenticationConfig)
.Build();
}
}
//todo - should not return null?
return null;
return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes);
}
}
}

View File

@ -1,4 +1,4 @@
using Ocelot.Creator.Configuration;
/*using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator
{
@ -34,4 +34,4 @@ namespace Ocelot.Configuration.Creator
.WithRequireHttps(authenticationOptions.IdentityServerConfig.RequireHttps).Build();
}
}
}
}*/

View File

@ -92,7 +92,7 @@ namespace Ocelot.Configuration.Creator
private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
{
var response = _configurationValidator.IsValid(fileConfiguration);
var response = await _configurationValidator.IsValid(fileConfiguration);
if (response.Data.IsError)
{
@ -110,14 +110,14 @@ namespace Ocelot.Configuration.Creator
foreach (var reRoute in fileConfiguration.ReRoutes)
{
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration, fileConfiguration.AuthenticationOptions);
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
reRoutes.Add(ocelotReRoute);
}
return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath);
}
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration, List<FileAuthenticationOptions> authOptions)
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
{
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
@ -129,7 +129,7 @@ namespace Ocelot.Configuration.Creator
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileReRoute, globalConfiguration);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute, authOptions);
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
@ -168,7 +168,7 @@ namespace Ocelot.Configuration.Creator
.WithQosOptions(qosOptions)
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
.WithRateLimitOptions(rateLimitOption)
.WithAuthenticationProviderKey(fileReRoute.AuthenticationProviderKey)
.WithAuthenticationProviderKey(fileReRoute.AuthenticationOptions.AuthenticationProviderKey)
.Build();
await SetupLoadBalancer(reRoute);

View File

@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator
{
public interface IAuthenticationOptionsCreator
{
AuthenticationOptions Create(FileReRoute reRoute, List<FileAuthenticationOptions> authOptions);
AuthenticationOptions Create(FileReRoute reRoute);
}
}

View File

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

View File

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

View File

@ -8,11 +8,9 @@ namespace Ocelot.Configuration.File
{
ReRoutes = new List<FileReRoute>();
GlobalConfiguration = new FileGlobalConfiguration();
AuthenticationOptions = new List<FileAuthenticationOptions>();
}
public List<FileReRoute> ReRoutes { get; set; }
public FileGlobalConfiguration GlobalConfiguration { get; set; }
public List<FileAuthenticationOptions> AuthenticationOptions { get; set; }
}
}

View File

@ -14,6 +14,7 @@ namespace Ocelot.Configuration.File
FileCacheOptions = new FileCacheOptions();
QoSOptions = new FileQoSOptions();
RateLimitOptions = new FileRateLimitRule();
AuthenticationOptions = new FileAuthenticationOptions();
}
public string DownstreamPathTemplate { get; set; }
@ -33,6 +34,6 @@ namespace Ocelot.Configuration.File
public FileQoSOptions QoSOptions { get; set; }
public string LoadBalancer {get;set;}
public FileRateLimitRule RateLimitOptions { get; set; }
public string AuthenticationProviderKey {get; set;}
public FileAuthenticationOptions AuthenticationOptions { get; set; }
}
}

View File

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

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Ocelot.Authentication.Handler;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File;
using Ocelot.Errors;
using Ocelot.Responses;
@ -10,7 +10,14 @@ namespace Ocelot.Configuration.Validator
{
public class FileConfigurationValidator : IConfigurationValidator
{
public Response<ConfigurationValidationResult> IsValid(FileConfiguration configuration)
private readonly IAuthenticationSchemeProvider _provider;
public FileConfigurationValidator(IAuthenticationSchemeProvider provider)
{
_provider = provider;
}
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
{
var result = CheckForDuplicateReRoutes(configuration);
@ -19,7 +26,7 @@ namespace Ocelot.Configuration.Validator
return new OkResponse<ConfigurationValidationResult>(result);
}
result = CheckForUnsupportedAuthenticationProviders(configuration);
result = await CheckForUnsupportedAuthenticationProviders(configuration);
if (result.IsError)
{
@ -42,38 +49,27 @@ namespace Ocelot.Configuration.Validator
return new OkResponse<ConfigurationValidationResult>(result);
}
private ConfigurationValidationResult CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration)
private async Task<ConfigurationValidationResult> CheckForUnsupportedAuthenticationProviders(FileConfiguration configuration)
{
var errors = new List<Error>();
//todo - these loops break seperation of concerns...unit tests should fail also..
foreach(var authProvider in configuration.AuthenticationOptions)
{
if (IsSupportedAuthenticationProvider(authProvider.Provider))
{
continue;
}
var error = new UnsupportedAuthenticationProviderError($"{authProvider.Provider} is unsupported authentication provider");
errors.Add(error);
}
foreach (var reRoute in configuration.ReRoutes)
{
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationProviderKey);
var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions.AuthenticationProviderKey);
if (!isAuthenticated)
{
continue;
}
//todo is this correct?
if(configuration.AuthenticationOptions.Exists(x => x.AuthenticationProviderKey == reRoute.AuthenticationProviderKey))
var data = await _provider.GetAllSchemesAsync();
var schemes = data.ToList();
if (schemes.Any(x => x.Name == reRoute.AuthenticationOptions.AuthenticationProviderKey))
{
continue;
}
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationProviderKey} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
var error = new UnsupportedAuthenticationProviderError($"{reRoute.AuthenticationOptions.AuthenticationProviderKey} is unsupported authentication provider, upstream template is {reRoute.UpstreamPathTemplate}, upstream method is {reRoute.UpstreamHttpMethod}");
errors.Add(error);
}
@ -82,13 +78,6 @@ namespace Ocelot.Configuration.Validator
: new ConfigurationValidationResult(false);
}
private bool IsSupportedAuthenticationProvider(string provider)
{
SupportedAuthenticationProviders supportedProvider;
return Enum.TryParse(provider, true, out supportedProvider);
}
private ConfigurationValidationResult CheckForReRoutesContainingDownstreamSchemeInDownstreamPathTemplate(FileConfiguration configuration)
{
var errors = new List<Error>();

View File

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

View File

@ -1,4 +1,4 @@
using Ocelot.Configuration;
/*using Ocelot.Configuration;
using Ocelot.Configuration.File;
namespace Ocelot.Creator.Configuration
@ -7,4 +7,4 @@ namespace Ocelot.Creator.Configuration
{
IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions);
}
}
}*/

View File

@ -42,15 +42,10 @@ using System.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.IdentityModel.Tokens;
using Ocelot.Configuration;
using Ocelot.Creator.Configuration;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using System.IO;
using Newtonsoft.Json;
namespace Ocelot.DependencyInjection
{
@ -77,7 +72,6 @@ namespace Ocelot.DependencyInjection
services.Configure<FileConfiguration>(configurationRoot);
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
services.TryAddSingleton<IAuthenticationProviderConfigCreator, AuthenticationProviderConfigCreator>();
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
@ -149,38 +143,6 @@ namespace Ocelot.DependencyInjection
services.AddIdentityServer(identityServerConfiguration, configurationRoot);
}
//todo - this means we need to break auth providers into there own section in the config
//then join onto them from reroutes based on a key
var data = File.ReadAllText("configuration.json");
var config = JsonConvert.DeserializeObject<FileConfiguration>(data);
foreach(var authOptions in config.AuthenticationOptions)
{
if(authOptions.Provider.ToLower() == "identityserver")
{
Action<IdentityServerAuthenticationOptions> options = o =>
{
o.Authority = authOptions.IdentityServerConfig.ProviderRootUrl;
o.ApiName = authOptions.IdentityServerConfig.ApiName;
o.RequireHttpsMetadata = authOptions.IdentityServerConfig.RequireHttps;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = authOptions.IdentityServerConfig.ApiSecret;
};
services.AddAuthentication()
.AddIdentityServerAuthentication(authOptions.AuthenticationProviderKey, options);
}
else if (authOptions.Provider.ToLower() == "jwt")
{
services.AddAuthentication()
.AddJwtBearer(x =>
{
x.Authority = authOptions.JwtConfig.Authority;
x.Audience = authOptions.JwtConfig.Audience;
});
}
}
return services;
}

View File

@ -26,7 +26,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.0.2" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
@ -42,7 +42,7 @@
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Consul" Version="0.7.2.3" />
<PackageReference Include="Polly" Version="5.3.1" />
<PackageReference Include="IdentityServer4" Version="2.0.1" />
<PackageReference Include="IdentityServer4" Version="2.0.2" />
</ItemGroup>
</Project>