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
Patterns support for allowed subjects by the JWT realm #102426
Patterns support for allowed subjects by the JWT realm #102426
Conversation
allowed_subjects
settingallowed_subjects
setting
allowed_subjects
settingallowed_subjects
setting
allowed_subjects
settingallowed_subject_patterns
setting
allowed_subject_patterns
setting
Pinging @elastic/es-security (Team:Security) |
Hi @albertzaharovits, I've created a changelog YAML for you. |
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.
Overall functional changes are looking good. I left a few non-blocking comments for the code change. 👍
I still need to review tests and documentation changes. Just wanted to leave the initial feedback first.
...curity/src/main/java/org/elasticsearch/xpack/security/authc/jwt/JwtStringClaimValidator.java
Outdated
Show resolved
Hide resolved
) { | ||
this.claimName = claimName; | ||
this.fallbackClaimNames = fallbackClaimNames; | ||
this.allowedClaimValues = allowedClaimValues; |
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.
nit: The allowedClaimValues
is not nullable anymore and will cause NPE when used below. We do validate it before constructing the validator, but maybe we should assert
it as well. Same is for newly added allowedClaimValuePatterns
.
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 pushed the asserts, but in principal I prefer we annotate nullable parameters and assume the others cannot be null.
@@ -63,16 +85,14 @@ public void validate(JWSHeader jwsHeader, JWTClaimsSet jwtClaimsSet) { | |||
if (claimValues == null) { | |||
throw new IllegalArgumentException("missing required string claim [" + fallbackableClaim + "]"); | |||
} | |||
|
|||
if (allowedClaimValues != null && false == claimValues.stream().anyMatch(allowedClaimValues::contains)) { | |||
if (false == claimValues.stream().anyMatch(allowedClaimValuesPredicate)) { |
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 like that we don't accept null
anymore, but the null
here was nicely short-circuiting the check to mean allow all and avoid any checks. Maybe we could do something similar and introduce a new private constructor which instead of allowedClaimValues
and allowedClaimValuePatterns
collections simply accepts allowedClaimValuesPredicate
. This way we can create ALLOW_ALL_SUBJECTS
which always returns true
and thus avoid string matching against *
.
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.
On a second thought, this would not solve it completely. The proper way to short-circuit it would be if allowedClaimValuesPredicate
accepted a list of strings to check.
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 *
used in the ALLOW_ALL_SUBJECTS
benefits of some short-circuting of sorts:
Lines 214 to 216 in 5c26cf0
} else if (pattern.equals("*")) { | |
return MATCH_ALL; | |
} else { |
In the JwtStringClaimValidator
constructor I could short-circuit the predicate building logic if either one of the "allowed" lists is empty, but I think that would complicate the logic for very little benefit (e.g. I expect the JVM will figure out that set stays empty and expedite the contains
check).
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.
Wasn't aware of a short-circuit in Automatons
. Agreed, let's keep it simple and optimize if we need to.
); | ||
} | ||
|
||
private static void validateAllowedSubjectsSettings(RealmConfig realmConfig) { |
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.
optional: I don't feel strongly, but we could potentially move this settings validation to JwtRealmSetting
class and implement a custom Setting.Validator
for both allowed_subjects
and allowed_subject_patterns
.
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.
Good point. I've pushed f095252 , but the "richness" of the error description decreased because it's not possible to tell if the dependent setting (of some other setting) is specified or not (validation has access to the default value in case it's not specified).
So now the error message only tells you that one of allowed_subjects
or allowed_subject_patterns
must be specified and not be empty.
`allowed_subject_patterns`:: | ||
Analogous to `allowed_subjects` but it accepts a list of <<regexp-syntax,Lucene regexp>> | ||
and wildcards for the allowed JWT subjects. Wildcards use the `*` and `?` special | ||
characters (which are escaped by `\`) to mean "any string" and "any single character" |
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.
optional: Would be nice to include a small example (similarly like you did for Lucene regex) that shows usage of both wildcards (including escaping).
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.
Pushed cc62b3b .
@@ -167,6 +180,253 @@ public void testDoesNotSupportWildcardOrRegex() throws ParseException { | |||
} | |||
} | |||
|
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.
Nice job on testing.
small nit: We could potentially add tests which demonstrate that:
sub
validation is case sensitive?
can be used as a single special character that must be present- an empty (
""
)sub
is rejected
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.
Additional test suggestion, we could adjust existing JwtRestIT integration test to randomly include allowed_subjects
or allowed_subject_patterns
(e.g. service_*@app?.example.com
).
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.
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 👍
Left a couple of non-blocking nits and suggestions. Feel free to address at your discretion.
Co-authored-by: Slobodan Adamović <slobodanadamovic@users.noreply.github.com>
Co-authored-by: Slobodan Adamović <slobodanadamovic@users.noreply.github.com>
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
try { | ||
return Automatons.predicate(patterns); | ||
} catch (Exception e) { | ||
throw new SettingsException("Invalid patterns for allowed claim values for [" + claimName + "].", e); |
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.
nit: should this also include the fallback sub if it is defined ?
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.
Maybe, if we go with also using patterns for more claims in the future. I'm not convinced for subjects only, today, it's warranted, and in any case, it's slightly messy to implement.
This error shows up when the allowed_subject_patterns
is invalid, and it will show up in the logs as: "Invalid patterns for allowed claim values for [sub]."
Even if the settings also contain something such as fallback_claims.sub: client_id
I think the error is telling enough.
This adds support for allowing JWT token
sub
claims with Lucene patterns and wildcards,by introducing a new JWT realm setting
allowed_subject_patterns
that can be usedalongside the exist
allowed_subjects
realm setting.