SCIM: PATCH operations list has no size limit — missing maxOperations…#50872
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a fixed limit for SCIM PATCH operations to mitigate unbounded request processing.
Changes:
- Limits PATCH requests to 100 operations.
- Adds an integration test for exceeding the limit.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
AbstractScimResourceTypeProvider.java |
Enforces the PATCH operation limit. |
UserTest.java |
Tests rejection above the limit. |
|
|
||
| public abstract class AbstractScimResourceTypeProvider<M extends Model, R extends ResourceTypeRepresentation> implements ScimResourceTypeProvider<R> { | ||
|
|
||
| public static final int MAX_PATCH_OPERATIONS = 100; |
There was a problem hiding this comment.
@martin-kanis I think this is a valid point: the /ServiceProviderConfig endpoint will advertise maxOperations: 0 (meaning "no limit" or "not supported" depending on interpretation) while the server actually enforces 100. These should be consistent. We do something very similar for the filter's maxResults config. The value that is enforced is returned in the ServiceProviderConfig even if filtering is disabled.
There was a problem hiding this comment.
After further reviewing, I think this is not a valid suggestion. Mixing bulk parameters with patch in the provider config is semantically wrong. There's nothing in the RFC that suggests patch should advertise its max operations constraint via the standard ServiceProviderConfig.
| if (operations.size() > MAX_PATCH_OPERATIONS) { | ||
| throw new ModelValidationException( | ||
| "PATCH request exceeds maximum allowed number of %d operations".formatted(MAX_PATCH_OPERATIONS)); |
There was a problem hiding this comment.
The issue does mention bounding member/value arrays, so it's a legitimate gap in the PR. However, I would say this is a separate concern from the top-level operation count and could be addressed in a follow-up. Raising it as a suggestion for a separate PR is fair, blocking this PR on it seems a bit excessive.
| throw new ModelValidationException( | ||
| "PATCH request exceeds maximum allowed number of %d operations".formatted(MAX_PATCH_OPERATIONS)); |
There was a problem hiding this comment.
Seems valid to me - The issue explicitly requests scimType=tooMany per RFC 7644 section 3.12. This is a real spec-compliance gap. We should handle the exception differently for this case. The test should also assert the scimType value, not just the status code and message substring.
31c2d89 to
6cd8d67
Compare
| public static class PatchSupport extends Supported { | ||
| @JsonProperty("maxOperations") | ||
| private Integer maxOperations = 0; |
There was a problem hiding this comment.
@martin-kanis I was originally thinking about using the maxOperations from bulk instead of adding it to patch in the service provider config, following Copilot's initial review. However, thinking about it more carefully, it seems to me that there are issues with the original suggestion and also with this approach you've taken:
bulk.maxOperationsis specifically about bulk endpoint operations, not PATCH operations. Using it for PATCH would be semantically wrong (approach copilot initially suggested)- adding a non-standard
maxOperationsfield underpatchin theServiceProviderConfigresponse risks breaking strict SCIM clients that model the patch object per the RFC schema (e.g., Jackson withFAIL_ON_UNKNOWN_PROPERTIES = truewould throw on the unknown attribute).
So all of this to say that I now believe it is safer to enforce the limit server-side without advertising it in the config at all, as you originally did. So I'm basically asking you to revert back to the previous approach. Sorry about this, I thought it would be good to allow clients to discover the maximum patch size via service provider config without considering clients might have problems parsing the config if we use non-standard parameters.
There was a problem hiding this comment.
No problem. I reverted it to the previous version and I added some notes to the documentation and to javadoc.
|
@sguilhen Thanks for the review and suggestions. I updated the PR with your proposals (I hope, I got them right) except the one that can be done as follow-up. |
6cd8d67 to
23b99a1
Compare
| if (operations.size() > MAX_PATCH_OPERATIONS) { | ||
| throw new ScimPatchException( | ||
| "PATCH request exceeds maximum allowed number of %d operations".formatted(MAX_PATCH_OPERATIONS)); |
There was a problem hiding this comment.
Not an issue, every patch-capable provider extends the same abstract class and future providers (if any) will follow the same pattern
sguilhen
left a comment
There was a problem hiding this comment.
Thanks @martin-kanis! LGTM
… enforcement Closes keycloak#50475 Signed-off-by: Martin Kanis <mkanis@ibm.com>
23b99a1 to
bc3c390
Compare
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.forms.BrowserFlowTest#testUserWithOneAdditionalFactorOtpSuccessorg.keycloak.testsuite.oauth.ClientAuthPostMethodTest#testPostAuthenticationNotAllowedWhenBasicRequestedorg.keycloak.testsuite.oauth.ClientAuthPostMethodTest#testBasicAuthenticationNotAllowedWhenPostRequested |
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.forms.BrowserFlowTest#testUserWithOneAdditionalFactorOtpSuccessorg.keycloak.testsuite.oauth.ClientAuthPostMethodTest#testPostAuthenticationNotAllowedWhenBasicRequestedorg.keycloak.testsuite.oauth.ClientAuthPostMethodTest#testBasicAuthenticationNotAllowedWhenPostRequested |
… enforcement
Closes #50475