Skip to content

Commit

Permalink
Implemented Security middleware (issue #8) and JWT token validation (…
Browse files Browse the repository at this point in the history
…issue #21)
  • Loading branch information
mohammad-shaddad committed Dec 2, 2019
1 parent 4c895d0 commit 992e8ed
Show file tree
Hide file tree
Showing 21 changed files with 687 additions and 12 deletions.
5 changes: 5 additions & 0 deletions src/Ianvs/Common/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ public class Endpoint
/// An alternative random balancer mode used to select the server to service this endpoint. If a load balancer mode is specified at the Root level, it will be overridden by this value. If no mode is specified at any level, default is random
/// </summary>
public string LoadBalancerMethod { get; set; }

/// <summary>
/// The list of security schemes used to secure the operation
/// </summary>
public List<SecurityRequirement> Security { get; set; }
}
}
5 changes: 5 additions & 0 deletions src/Ianvs/Common/IanvsContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Onyx.Ianvs.Dispatch;
using System.Security.Claims;

namespace Onyx.Ianvs.Common
{
Expand All @@ -31,5 +32,9 @@ public class IanvsContext
public int StatusCode { get; internal set; } = 200;
public string TrackId { get; set; }
public long ProcessingTime { get; internal set; }
public SecurityScheme SecurityScheme { get; internal set; }
public List<SecurityRequirement> Security { get; set; }
public SecurityRequirement SecurityRequirement { get; internal set; }
public ClaimsPrincipal Principal { get; internal set; }
}
}
5 changes: 5 additions & 0 deletions src/Ianvs/Common/Operation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,10 @@ public class Operation
/// A list of parameters that are applicable for this operation. If a parameter is already defined at the Path Item, the new definition will override it but can never remove it
/// </summary>
public List<Parameter> Parameters { get; set; }

/// <summary>
/// The list of security schemes used to secure the operation
/// </summary>
public List<SecurityRequirement> Security { get; set; }
}
}
23 changes: 23 additions & 0 deletions src/Ianvs/Common/SecurityRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Onyx.Ianvs.Common
{
/// <summary>
/// Represents a Security Requirements Open Api object
/// </summary>
public class SecurityRequirement
{
/// <summary>
/// The required security scheme name
/// </summary>
public string SchemeName { get; set;}

/// <summary>
/// The authorizations scopes that must be granted to the caller
/// </summary>
public string[] Scopes { get; set; }
}
}
53 changes: 53 additions & 0 deletions src/Ianvs/Common/SecurityScheme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Onyx.Ianvs.Common
{
/// <summary>
/// Represents an Open API Security Scheme object
/// </summary>
public class SecurityScheme
{
/// <summary>
/// The security scheme name
/// </summary>
public string Name { get; set; }

/// <summary>
/// The security scheme type
/// </summary>
public string Type { get; set; }

/// <summary>
/// Where the authentication type is sent in the request; supported values: header, cookie
/// </summary>
public string In { get; set; }

/// <summary>
/// The security scheme; bearer, oauth, key, etc.
/// </summary>
public string Scheme { get; set; }

/// <summary>
/// Used as a hint for the value name: bearer, cookie name
/// </summary>
public string BearerFormat { get; set; }

/// <summary>
/// The Url of the auth authority openId configuration discovery Url
/// </summary>
public string OpenIdConnectUrl { get; set; }

/// <summary>
/// The Url of the auth credentials issuer/authority
/// </summary>
public string Issuer { get; set; }

/// <summary>
/// The list of valid audiences for the security scheme
/// </summary>
public string[] Audiences { get; set; }
}
}
5 changes: 5 additions & 0 deletions src/Ianvs/Configuration/IanvsConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ public class IanvsConfiguration
/// The default load balancer mode used to select the server to service operations. Default is random
/// </summary>
public string LoadBalancerMethod { get; set; } = "random";

/// <summary>
/// The list of defined security schemes
/// </summary>
public List<SecurityScheme> SecuritySchemes { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/Ianvs/Configuration/IanvsMeta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,16 @@ public static class IanvsMeta

public const string E_CONFIG_VIRTUAL_ENDPOINT = "x-ianvs-endpoint";
public const string E_CONFIG_LOAD_BALANCER_METHOD = "x-ianvs-lb-method";

public const string E_CONFIG_COMPONENTS = "components";

public const string E_CONFIG_SECURITY_SCHEMES = "securitySchemes";
public const string E_CONFIG_SEC_SCHEME_TYPE = "type";
public const string E_CONFIG_SEC_SCHEME_IN = "in";
public const string E_CONFIG_SEC_SCHEME_OPEN_ID_CONNECT_URL = "openIdConnectUrl";
public const string E_CONFIG_SEC_SCHEME_ISSUER = "x-issuer";
public const string E_CONFIG_SEC_SCHEME_AUDIENCES = "x-audience";

public const string E_CONFIG_OPER_SECURITY = "security";
}
}
8 changes: 8 additions & 0 deletions src/Ianvs/Configuration/Json/IanvsJsonConfigurationParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public static IanvsConfiguration Parse(string data)
ianvs.LoadBalancerMethod = jLoadBalancerMethod.ToString();
}

if (jIanvs.TryGetProperty(IanvsMeta.E_CONFIG_COMPONENTS, out JsonElement components))
{
if(components.TryGetProperty(IanvsMeta.E_CONFIG_SECURITY_SCHEMES, out JsonElement securitySchemes))
{
ianvs.SecuritySchemes = JsonSecuritySchemeParser.Parse(securitySchemes);
}
}

return ianvs;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Ianvs/Configuration/Json/JsonOperationParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static Operation Parse(JsonProperty operationData)
if (property.Name == "servers") operation.Servers = JsonServerParser.Parse(property.Value);
if (property.Name == IanvsMeta.E_CONFIG_LOAD_BALANCER_METHOD) operation.LoadBalancerMethod = property.Value.ToString();
if (property.Name == "parameters") operation.Parameters = JsonParameterParser.Parse(property.Value);
if (property.Name == IanvsMeta.E_CONFIG_OPER_SECURITY) operation.Security = JsonSecurityRequirementParser.Parse(property.Value);
}
return operation;
}
Expand Down
47 changes: 47 additions & 0 deletions src/Ianvs/Configuration/Json/JsonSecurityRequirementParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Onyx.Ianvs.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

namespace Onyx.Ianvs.Configuration.Json
{
public class JsonSecurityRequirementParser
{
/// <summary>
/// Parses a JSON representation of an Open API Security Requirement object
/// </summary>
/// <param name="serversData">JSON representation of an Open API Security Requirement object</param>
/// <returns>An Open API Security Requirement object</returns>
public static List<SecurityRequirement> Parse(JsonElement securityData)
{
List<SecurityRequirement> security = new List<SecurityRequirement>();
foreach (JsonElement scheme in securityData.EnumerateArray())
{
security.Add(ParseSecurityRequirement(scheme));
}
return security;
}

private static SecurityRequirement ParseSecurityRequirement(JsonElement definition)
{
foreach (JsonProperty item in definition.EnumerateObject())
{
SecurityRequirement scheme = new SecurityRequirement()
{
SchemeName = item.Name,
Scopes = new string[item.Value.GetArrayLength()]
};

int i = 0;
foreach (JsonElement element in item.Value.EnumerateArray())
{
scheme.Scopes[i++] = element.GetString();
}
return scheme;
}
return null;
}
}
}
51 changes: 51 additions & 0 deletions src/Ianvs/Configuration/Json/JsonSecuritySchemeParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Onyx.Ianvs.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

namespace Onyx.Ianvs.Configuration.Json
{
public class JsonSecuritySchemeParser
{
/// <summary>
/// Parses a JSON representation of an Open API Security Schemes object
/// </summary>
/// <param name="serversData">JSON representation of an Open API Security Schemes object</param>
/// <returns>An Open API Security Schemes object</returns>
public static List<SecurityScheme> Parse(JsonElement securitySchemesData)
{
List<SecurityScheme> securitySchemes = new List<SecurityScheme>();
foreach (JsonProperty scheme in securitySchemesData.EnumerateObject())
{
securitySchemes.Add(ParseSecurityScheme(scheme));
}
return securitySchemes;
}

private static SecurityScheme ParseSecurityScheme(JsonProperty definition)
{
SecurityScheme scheme = new SecurityScheme()
{
Name = definition.Name
};
foreach (JsonProperty property in definition.Value.EnumerateObject())
{
if (property.Name == IanvsMeta.E_CONFIG_SEC_SCHEME_TYPE) scheme.Type = property.Value.GetString();
if (property.Name == IanvsMeta.E_CONFIG_SEC_SCHEME_IN) scheme.In = property.Value.GetString();
if (property.Name == IanvsMeta.E_CONFIG_SEC_SCHEME_OPEN_ID_CONNECT_URL) scheme.OpenIdConnectUrl = property.Value.GetString();
if (property.Name == IanvsMeta.E_CONFIG_SEC_SCHEME_ISSUER) scheme.Issuer = property.Value.GetString();
if (property.Name == IanvsMeta.E_CONFIG_SEC_SCHEME_AUDIENCES)
{
scheme.Audiences = new string[property.Value.GetArrayLength()];
for (int i = 0; i < scheme.Audiences.Length; i++)
{
scheme.Audiences[i] = property.Value[i].GetString();
}
}
}
return scheme;
}
}
}
5 changes: 5 additions & 0 deletions src/Ianvs/Configuration/Store/IIanvsConfigurationStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ public interface IIanvsConfigurationStore
/// The default load balancer mode used to select the server to service operations. Default is random
/// </summary>
public string LoadBalancerMethod { get; set; }

/// <summary>
/// The list of defined security schemes
/// </summary>
public List<SecurityScheme> SecuritySchemes { get; set; }
}
}
9 changes: 9 additions & 0 deletions src/Ianvs/Configuration/Store/IanvsFileConfigurationStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ public string LoadBalancerMethod
set { }
}

/// <summary>
/// The list of defined security schemes
/// </summary>
public List<SecurityScheme> SecuritySchemes
{
get { return ianvsConfiguration.SecuritySchemes; }
set { }
}

public IanvsFileConfigurationStore()
{
string data = File.ReadAllText("./ianvs.json");
Expand Down
33 changes: 30 additions & 3 deletions src/Ianvs/IanvsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using Onyx.Ianvs.Configuration.Store;
using Onyx.Ianvs.Transformation;
using Onyx.Ianvs.Dispatch;
using Onyx.Ianvs.Security;
using Onyx.Ianvs.Security.Jwt;

namespace Onyx.Ianvs
{
Expand All @@ -31,6 +33,7 @@ public static IApplicationBuilder UseIanvs(this IApplicationBuilder app)
{
app.UseMiddleware<IngressMiddleware>();
app.UseMiddleware<RoutingMiddleware>();
app.UseMiddleware<SecurityMiddleware>();
app.UseMiddleware<TransformationMiddleware>();
app.UseMiddleware<LoadBalancingMiddleware>();
app.UseMiddleware<EgressMiddleware>();
Expand All @@ -44,14 +47,38 @@ public static IApplicationBuilder UseIanvs(this IApplicationBuilder app)
public static void AddIanvs(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
// Add Core services
AddIanvsCore(services);
// Add Authentication services
AddIanvsAuthentication(services);
// Add Load balancing services
AddIanvsLoadBalancing(services);
// Add Egress services
AddIanvsEgress(services);
}

private static void AddIanvsCore(IServiceCollection services)
{
services.TryAddScoped<IanvsContext>();

services.TryAddSingleton<IIanvsConfigurationStore, IanvsFileConfigurationStore>();
services.TryAddSingleton<LoadBalancerFactory>();
services.TryAddSingleton<RandomLoadBalancer>();
}

private static void AddIanvsEgress(IServiceCollection services)
{
services.TryAddSingleton<DispatcherFactory>();
services.TryAddSingleton<HttpDispatcher>();
}

private static void AddIanvsAuthentication(IServiceCollection services)
{
services.TryAddSingleton<AuthenticatorFactory>();
services.TryAddSingleton<JwtAuthenticationHandler>();
}

private static void AddIanvsLoadBalancing(IServiceCollection services)
{
services.TryAddSingleton<LoadBalancerFactory>();
services.TryAddSingleton<RandomLoadBalancer>();
}
}
}
1 change: 1 addition & 0 deletions src/Ianvs/Onyx.Ianvs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="5.6.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.5" />
</ItemGroup>

Expand Down
Loading

0 comments on commit 992e8ed

Please sign in to comment.