+dynamic claim variables (#855)

incl. tests
This commit is contained in:
Michel Bretschneider
2019-04-15 10:51:34 +02:00
committed by Thiago Loureiro
parent 340d0de233
commit 639011bc62
5 changed files with 127 additions and 15 deletions

View File

@ -1,6 +1,13 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Middleware;
using Ocelot.Responses;
using Ocelot.Values;
namespace Ocelot.Authorisation
{
@ -15,8 +22,11 @@ namespace Ocelot.Authorisation
_claimsParser = claimsParser;
}
public Response<bool> Authorise(ClaimsPrincipal claimsPrincipal, Dictionary<string, string> routeClaimsRequirement)
{
public Response<bool> Authorise(
ClaimsPrincipal claimsPrincipal,
Dictionary<string, string> routeClaimsRequirement,
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
){
foreach (var required in routeClaimsRequirement)
{
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key);
@ -27,12 +37,50 @@ namespace Ocelot.Authorisation
}
if (values.Data != null)
{
var authorised = values.Data.Contains(required.Value);
if (!authorised)
{
// dynamic claim
var match = Regex.Match(required.Value, @"^{(?<variable>.+)}$");
if (match.Success)
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
var variableName = match.Captures[0].Value;
var matchingPlaceholders = urlPathPlaceholderNameAndValues.Where(p => p.Name.Equals(variableName)).Take(2).ToArray();
if (matchingPlaceholders.Length == 1)
{
// match
var actualValue = matchingPlaceholders[0].Value;
var authorised = values.Data.Contains(actualValue);
if (!authorised)
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
$"dynamic claim value for {variableName} of {string.Join(", ", values.Data)} is not the same as required value: {actualValue}"));
}
}
else
{
// config error
if (matchingPlaceholders.Length == 0)
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
$"config error: requires variable claim value: {variableName} placeholders does not contain that variable: {string.Join(", ", urlPathPlaceholderNameAndValues.Select(p=>p.Name))}"));
}
else
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
$"config error: requires variable claim value: {required.Value} but placeholders are ambiguous: {string.Join(", ", urlPathPlaceholderNameAndValues.Where(p=>p.Name.Equals(variableName)).Select(p => p.Value))}"));
}
}
}
else
{
// static claim
var authorised = values.Data.Contains(required.Value);
if (!authorised)
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorisedError(
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
}
}
}
else

View File

@ -1,5 +1,9 @@
using System.Security.Claims;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Responses;
using Ocelot.Values;
namespace Ocelot.Authorisation
{
@ -7,6 +11,10 @@ namespace Ocelot.Authorisation
public interface IClaimsAuthoriser
{
Response<bool> Authorise(ClaimsPrincipal claimsPrincipal, Dictionary<string, string> routeClaimsRequirement);
Response<bool> Authorise(
ClaimsPrincipal claimsPrincipal,
Dictionary<string, string> routeClaimsRequirement,
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
);
}
}

View File

@ -57,8 +57,8 @@
if (IsAuthorisedRoute(context.DownstreamReRoute))
{
Logger.LogInformation("route is authorised");
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement);
var authorised = _claimsAuthoriser.Authorise(context.HttpContext.User, context.DownstreamReRoute.RouteClaimsRequirement, context.TemplatePlaceholderNameAndValues);
if (authorised.IsError)
{