-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple values for single key in RouteClaimsRequirement #746
Comments
I believe adding more sophisticated route claims requirements in general would help with this, will discuss with the team best approaches. |
Is this fixed? |
I'm interested too. Does another workaround exist? |
as workaround override authorisation middleware for claims or policy based with claims in policies as suggested here |
I've tried the override authorisation middleware method , but the claims are strictly converted in a Dictionary<string,string> format before the middleware was called ; This format break the Route because the value cannot be converted into a string; "RouteClaimsRequirement": { "Role": ["User", "Admin"] } This format also don't work because the Role key in dictionary will be ovverride with the second value; "RouteClaimsRequirement": { "Role": "Admin" "Role": "User" } so.. My personal solution will be "RouteClaimsRequirement": { "Role": "Admin , User" } In the authorisation middleware method , i will parse the string with a regex pattern to obtain the single role value: public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var configuration = new OcelotPipelineConfiguration
{
AuthorizationMiddleware = async (ctx, next) =>
{
if (this.Authorize(ctx))
{
await next.Invoke();
}
else {
// ctx.Errors.Add(new UnauthorisedError($"Fail to authorize"));
}
}
};
.
.
.
await app.UseOcelot(configuration);
} The logic of Authorize Method private bool Authorize(HttpContext ctx)
{
DownstreamRoute route = (DownstreamRoute)ctx.Items["DownstreamRoute"];
string key = route.AuthenticationOptions.AuthenticationProviderKey;
if (key == null || key == "") return true;
if (route.RouteClaimsRequirement.Count == 0) return true;
else
{
else {
//flag for authorization
bool auth = false;
//where are stored the claims of the jwt token
Claim[] claims = ctx.User.Claims.ToArray<Claim>();
//where are stored the required claims for the route
Dictionary<string, string> required = route.RouteClaimsRequirement;
.
.
((AUTHORIZATION LOGIC))
.
.
return auth;
} remeber to add in the ConfigureService method services.AddAuthorization();
services.AddAuthentication()
.AddJwtBearer("TestKey", x =>
{
// x.RequireHttpsMetadata = false;
x.TokenValidationParameters = tokenValidationParameters;
}); (I Still working on my Authorization logic that will implement the multiple claims with And/Or logic with regex of strings , but the claims data structure implemented with Dictionary<string , string> is very ugly and not very flexible) //updated for last version of ocelot |
((AUTHORIZATION LOGIC)) Example Regex reor = new Regex(@"[^,\s+$ ][^\,]*[^,\s+$ ]");
MatchCollection matches;
Regex reand = new Regex(@"[^&\s+$ ][^\&]*[^&\s+$ ]");
MatchCollection matchesand;
int cont = 0;
foreach (KeyValuePair<string, string> claim in required)
{
matches = reor.Matches(claim.Value);
foreach (Match match in matches)
{
matchesand = reand.Matches(match.Value);
cont = 0;
foreach (Match m in matchesand)
{
foreach (Claim cl in claims)
{
if (cl.Type == claim.Key)
{
if (cl.Value == m.Value)
{
cont++;
}
}
}
}
if (cont == matchesand.Count)
{
return true;
// break;
}
}
} Example of Configuration "RouteClaimsRequirement": { "Role": "User & IT , Admin" } Logic result : IF((User and IT) or Admin) |
May I please know any update on this? |
by using @arro000 's trick I changed a few things to make it work for me on .Net Core 3.1 & Ocelot 16.0.1. |
What is |
Is the instance of object with the bearer authentication rules, in my case for example: var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey
}; |
With this change do you still need to add |
Hi, is there any push new commit for this issue? |
@arro000 Should used |
Is it clear when this enhancement will be added. I am asking this question, because it has been over two years since this enhancement was requested. |
From where do you get the AuthorisationMiddleware, DownstreamContext and UnauthorisedError. |
I made this script for ocelot version 13, I updated my comment making it compatible with the latest version 17 |
No? |
Still no update on the topic ? |
Any progess on this issue? |
@Stians92 Do you have any solutions to fix this your opened issue over 4 years ago? |
Hi, I was able to make a few changes on this and get this to work for static claims by updating the The changes are basically updating all the public Response<bool> Authorize(
ClaimsPrincipal claimsPrincipal,
Dictionary<string, string[]> routeClaimsRequirement,
List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues
)
{
foreach (var required in routeClaimsRequirement)
{
var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key);
if (values.IsError)
{
return new ErrorResponse<bool>(values.Errors);
}
if (values.Data != null)
{
// dynamic claim
var match = Regex.Match(required.Value, @"^{(?<variable>.+)}$"); <-- how this line is supposed to be changed?
...........
...........
}
else
{
// static claim
var authorized = required.Value.Any(x=> values.Data.Contains(x));
if (!authorized)
{
return new ErrorResponse<bool>(new ClaimValueNotAuthorizedError(
$"claim value: {string.Join(", ", values.Data)} is not the same as required value: {required.Value} for type: {required.Key}"));
}
}
}
else
{
return new ErrorResponse<bool>(new UserDoesNotHaveClaimError($"user does not have claim {required.Key}"));
}
}
return new OkResponse<bool>(true);
} However, I am confused what should be the approach on the Dynamic claims? I did change it to var match = Regex.Match(required.Value!.FirstOrDefault() ?? string.Empty, "^{(?<variable>.+)}$"); which basically takes the first configured dynamic placeholder and moves on but am not sure if this is a good approach. |
@asrasya-kosha Will you open a PR? |
@raman-m yes, that is the intention. Thanks. |
You are assigned! |
Hi, is there any update about this feature? It is very interesting in my use case! |
Dear Pietro (@pietrocarpinacci-dufercodev), I am eager to see your contributions. Should you submit a PR with robust code and tests, I will give the issue priority. Currently, there are no PRs linked to this longstanding issue. However, it could be expedited with professional contributions from the community. Project Coordinator |
Hi Raman (@raman-m), Best Regards, |
@asrasya-kosha Asrasya, now it's your turn! 😉 |
I did start working on this but lost track because of work commitments.I will try and devote some time in next two weeks. |
@asrasya-kosha Fantastic, thank you, Asrasya! 🤩 |
New Feature
Allow claims to have an array value and not just a string value.
Motivation for New Feature
I have an application where I have multiple roles for my users. For endpoints that should only be reached by admins, I can use the following:
For endpoints that should only be reached by users, I can use the following:
For endpoints that should be reached by both users and admins I would like to use the following:
When I tried adding this, all requests to the endpoint respond with a 404.
This should let the request go through if the request has a claim for the role user or admin. This is equivalent to the built-in asp .net core attribute:
[Authorize(Roles = "User, Admin")]
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute.roles?view=aspnetcore-2.2
The text was updated successfully, but these errors were encountered: