-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Map only specific type of OIDC Claims #58524
Conversation
This commit changes our behavior in 2 ways: - When mapping claims to user properties ( principal, email, groups, name), we only handle string and array of string type. Previously we would fail to recognize an array of other types and that would cause failures when trying to cast to String. - When adding unmapped claims to the user metadata, we only handle string, number, boolean and arrays of these. Previously, we would fail to recognize an array of other types and that would cause failures when attempting to process role mappings. For user properties that are inherently single valued, like principal(username) we continue to support arrays of strings where we select the first one in case this is being depended on by users but we plan on removing this leniency in the next major release.
Pinging @elastic/es-security (:Security/Authentication) |
private static boolean isAllowedTypeForClaim(Object o) { | ||
return (o instanceof String || o instanceof Boolean || o instanceof Number | ||
|| (o instanceof Collection && ((Collection) o).stream() | ||
.allMatch(c -> c instanceof String || c instanceof Boolean || c instanceof Number))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably want to ensure that these are homogenous collections. I don't know how we would make sense of [ "1", true, 1.0 ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
think we probably want to ensure that these are heterogenous collections.
You used Greek so I get to be pedantic 🎉 s/heterogeneous/homogeneous/ :)
In all seriousness, I took a lenient stance here on purpose for metadata ( as opposed to user properties where I allowed only strings ).
I don't know how we would make sense of [ "1", true, 1.0 ]
- Metadata can be used as metadata, for informational purposes so we do not necessarily need to make sense of the value of a given key.
- It would work in a role mapping rule such as
"rules" : { "field" : { "groups" : "1" } },
happy to reconsider if you think this would cause an issue somewhere else or that it is too lenient for the sake of being lenient but I don't see any specific problem with it as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment clearly says homogeneous
😈
Fair enough - as long as it's a considered position, I'm not worried.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new validateClaimType
method looks a bit shaky to me. Could you please take another look? Thanks.
} else if (claimValueObject instanceof List == false) { | ||
throw new SettingsException("Setting [" + RealmSettings.getFullSettingKey(realmConfig, setting.getClaim()) | ||
+ " expects a claim with String or a String Array value but found a " | ||
+ claimValueObject.getClass().getName()); | ||
} | ||
return ((List<String>) claimValueObject).stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new validateClaimType
method does not assert claimValueObject
is a List
. In theory it could be a Set
or a Number
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validateClaimType
right above validates that is either a Collection or a String. Then, if this is not a String, it is a Collection. How could it get to be a Set or a Number ? Maybe I didn't get what you mean
private static void validateClaimType(Object claimValueObject, String settingKey) { | ||
if (claimValueObject instanceof String == false && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer a more descriptive method name if possible. The current feels like it will validate the claim value is a certain specified type. But it really just asserts the value is either:
- A String or
- If it is a collection, it must be a collection of String
Now I am not sure if this is what we really want? Is it OK if the value is a Number
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey can you elaborate on what you mean be "descriptive" here? The method does what it intends to do as described in the PR description:
When mapping claims to user properties ( principal, email, groups,
name), we only handle string and array of string type. Previously
we would fail to recognize an array of other types and that would
cause failures when trying to cast to String.
Doing so, seems to me to be "Validating the type of the claim value" and as such validateClaimType
seems appropriate. What would be an alternative that you are more comfortable with ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I mean is the name gave me the first impression that the signature would be something like:
void validateCliamType(Object value, Class<?> type)
But instead the type is not specified but should always be String or collection of it. So I'd personally prefer something like validateParsableClaim
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, I don't necessarily agree that validateClaimType is off :) but I don't dislike validateParsableClaim
enough to fortify my hill :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Ioannis. The more important piece of my comment here is about the behaviour, i.e. the if
check does not seem to do what we want to ensure, i.e. String
or Collection<String>
:
claimValueObject instanceof String == false &&
claimValueObject instanceof Collection &&
((Collection) claimValueObject).stream().allMatch(c -> c instanceof String) == false
If the value is a Number
, the if
branch will not be executed, i.e. a Number
will be considered as parsable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha! Now I saw it , thanks, will address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I commented a bit more about claimValueObject
checking.
} else if (claimValueObject instanceof Collection) { | ||
if (claimValueObject instanceof Collection && ((Collection) claimValueObject).stream().allMatch(c -> c instanceof String)) { | ||
values = (List<String>) claimValueObject; | ||
} else { | ||
throw new SettingsException("Setting [ " + settingKey + " expects a claim with String or a String Array value"); | ||
} | ||
} else { | ||
throw new SettingsException("Setting [ " + settingKey + " expects a claim with String or a String Array value"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The check
claimValueObject instanceof Collection
is performed twice, which does not seem to be necessary - The
if
check only ensure it is aCollection<String>
, not aList<String>
. So there is a theoretical possibility for cast exception.
I feel this part of code can be simplified to something like the follows:
} else if (claimValueObject instanceof Collection && ((Collection) claimValueObject).stream().allMatch(c -> c instanceof String)) {
values = (Collection<String>) claimValueObject;
} else {
throw new SettingsException("Setting [ " + settingKey + " expects a claim with String or a String Array value");
}
With above change, the return type of this method needs to be changed to Collection<String>
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeap you are right. In retrospect, I should have waited to fix this today and not last night. Thanks for calling me out !
I don't feel I need to review this - I'm happy if @ywangd is. |
@ywangd given that this is a bug fix, please feel free to merge this in my absense if you have bo additional comments on it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@ywangd given that this is a bug fix, please feel free to merge this in my absense if you have bo additional comments on it
Will merge and backport first thing on Monday.
This commit changes our behavior in 2 ways: - When mapping claims to user properties ( principal, email, groups, name), we only handle string and array of string type. Previously we would fail to recognize an array of other types and that would cause failures when trying to cast to String. - When adding unmapped claims to the user metadata, we only handle string, number, boolean and arrays of these. Previously, we would fail to recognize an array of other types and that would cause failures when attempting to process role mappings. For user properties that are inherently single valued, like principal(username) we continue to support arrays of strings where we select the first one in case this is being depended on by users but we plan on removing this leniency in the next major release.
This commit changes our behavior in 2 ways: - When mapping claims to user properties ( principal, email, groups, name), we only handle string and array of string type. Previously we would fail to recognize an array of other types and that would cause failures when trying to cast to String. - When adding unmapped claims to the user metadata, we only handle string, number, boolean and arrays of these. Previously, we would fail to recognize an array of other types and that would cause failures when attempting to process role mappings. For user properties that are inherently single valued, like principal(username) we continue to support arrays of strings where we select the first one in case this is being depended on by users but we plan on removing this leniency in the next major release. Co-authored-by: Ioannis Kakavas <[email protected]>
This commit changes our behavior in 2 ways:
name), we only handle string and array of string type. Previously
we would fail to recognize an array of other types and that would
cause failures when trying to cast to String.
string, number, boolean and arrays of these. Previously, we would
fail to recognize an array of other types and that would cause
failures when attempting to process role mappings.
For user properties that are inherently single valued, like
principal(username) we continue to support arrays of strings where
we select the first one in case this is being depended on by users
but we plan on removing this leniency in the next major release.