8000 User attribute value length extension by vramik · Pull Request #26196 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

User attribute value length extension - #26196

Merged
ahus1 merged 14 commits into
keycloak:mainfrom
vramik:9758-user-attr-length
Feb 16, 2024
Merged

ahus1 merged 14 commits into
keycloak:mainfrom
vramik:9758-user-attr-length

Conversation

@vramik
@vramik vramik commented Jan 15, 2024 •
Copy link
Copy Markdown
Contributor

Closes #9758

The PR adds a possibility to store and search by long user attribute values. It works for both regular and federated user attributes.

For both tables it uses two new columns in the database schema. One BLOB where the actual value is stored, second with its hash. On the hash column there is also an index created to allow effective search.

This approach could lead to very low chance that there would occur conflicts within the hash value, due to that there has been requirement to verify that user actually has indeed the specified value before returning it.

This check has some drawbacks:

  1. there is no way to perform the check on count queries as the result from the database contains the number for records fulfilling the criteria
  2. similarly there is no way to perform the check when calling UserAttributeFederatedStorage#getUsersByUserAttributeStream as it returns only user IDs (Stream<String>)
  3. effectively changing the characteristics for UserEntity - UserAttributeEntity associations from LAZY to EAGER as for each returned user its attributes needs to be loaded as well.

Ad. 3. for most scenarios we load attributes anyway as they are needed, but in case calling UsersResource#getUsers with briefRepresentation set to true (by default it is false) in has the mentioned impact

@vramik
vramik force-pushed the 9758-user-attr-length branch 2 times, most recently from 07bd213 to c17a3fe Compare January 15, 2024 14:29
@vramik
vramik marked this pull request as ready for review January 15, 2024 19:54
@vramik
vramik requested a review from a team January 15, 2024 19:54
@vramik
vramik requested review from a team as code owners January 15, 2024 19:54
@pedroigor
Copy link
Copy Markdown
Contributor

What about FED_USER_ATTRIBUTE?

@vramik
vramik force-pushed the 9758-user-attr-length branch from c17a3fe to f1248a3 Compare January 16, 2024 13:06
@vramik
vramik commented Jan 16, 2024
Copy link
Copy Markdown
Contributor Author

What about FED_USER_ATTRIBUTE?

Updated.

pedroigor
pedroigor previously approved these changes Jan 17, 2024
@vramik
vramik force-pushed the 9758-user-attr-length branch 2 times, most recently from b8f95a5 to 7c6bdd5 Compare January 18, 2024 12:46
ahus1
ahus1 previously requested changes Jan 22, 2024
@ahus1 ahus1 left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To prevent a denial of service attempt, we should have a maximum length of attributes defined.

We discussed that the declarative user profile could have a configurable length of an attribute. For the time being and also when UP is disabled, we need to have a (possibly hard coded) maximum length of user attributes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the comment, it looks like we previously supported searching for attributes longer than 255 characters in the federated attributes, and the code has changed to no longer support this. This would then be a regression. It is also difficult to explain to users why user attribute search lengths are different in both places.

Nitpick: I didn't find the method referenced here. It seems to me that the check is done in searchForUserByUserAttributeStream, therefore the comment might needs updating.

Suggested change
//this should never happen as the exception should be thrown already by JpaUserProvider#getUsersByUserAttributeStream, but leaving it here as additional check
//this should never happen as the exception should be thrown already by JpaUserProvider#searchForUserByUserAttributeStream, but leaving it here as additional check

@vramik
vramik marked this pull request as draft January 30, 2024 11:53
@ahus1
ahus1 commented Feb 13, 2024
Copy link
Copy Markdown
Member

@pedroigor - I found that when using LDAP, people were able to use longer user attributes (for example for photos) even before. The maximum length validation for unmanaged LDAP attributes applies here as well, so people who are using attribute values in LDAP which are larger than 2048 characters (ca. 1500 for binary data) will need to make those attributes managed and add a length validation.

I assume this is ok as no user should post an unlimited amount of data to a field in LDAP.

Tests are now green again, and docs updated.

@mhajas mhajas left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR @vramik and @ahus1. I added a few comments. Maybe I am nitpicking too much, sorry for that.

I also created a commit to refactor the lambda for hash collision check: mhajas@14d9e2f but I don't want to just push it as I am not sure it is much better, feel free to add it if you like it.


try (PreparedStatement ps = connection.prepareStatement("SELECT t.ID, t.VALUE" +
" FROM " + getTableName("FED_USER_ATTRIBUTE") + " t" +
" WHERE CHAR_LENGTH(t.VALUE) > 2024");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How sure are we that CHAR_LENGTH in MySQL returns the same results as String.length in Java?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unicode character have sometimes a strange behavior when it comes to lengths, and you're right that I should check this. See below for an example:

https://stackoverflow.com/questions/64213394/string-encoding-with-emoji-in-java

System.out.println("🇻🇺".length())
// -> 4
SELECT length("🇻🇺"), char_length("🇻🇺")
FROM dual;
-- -> 8, 2

I didn't find an equivalent for Java's length() in MySQL, so I'll do the following:

  • Change the SQL to use LENGTH() to select possibly more rows than necessary.
  • Add additional filtering in Java to ensure I'm updating only those where Java's string length is longer.

ahus1 and others added 3 commits February 14, 2024 09:59
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Signed-off-by: Michal Hajas <mhajas@redhat.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
@ahus1
ahus1 commented Feb 14, 2024
Copy link
Copy Markdown
Member

@mhajas - tests are green and your review comments have been addressed in separate commits.

Ready for another round of review. Thanks!

for (AttributeValidatorMetadata validator : metadata.getValidators()) {
if (validator.getValidatorId().equals(LengthValidator.ID)) {
lengthValidatorExists = true;
break;
@sguilhen sguilhen Feb 15, 2024 •
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this whole thing be simplified a bit by just returning from here instead of breaking out of two loops just to skip the rest of the method? i.e. remove the flag, and return if the id matches here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that work work. I'll add anther commit. The data is in-memory IMHO already, anyway.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit added. Build will hopefully be green. Ready for a final round of review?

public void searchByLongAttributeValue() {
testingClient.server().run(session -> {
// random string with suffix that makes it case-sensitive
String longValue = RandomStringUtils.random(2999, true, true) + "v";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, a question: shouldn't this size of attribute fail the length check? Or is a length validator added somewhere that I'm missing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK this creates a lambda that runs directly on the server and accesses the model. This doesn't go via the declarative user profile layer, so it can write any data it wants.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, true.. thanks for the info!

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
@ahus1
ahus1 requested a review from sguilhen February 15, 2024 17:18
@sguilhen sguilhen left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ahus1 ! I'm assuming the commits will be squashed upon merging - otherwise I would request you to fix the commit message that says "Fimpmlified method with a direct return" :)

Approving as long as CI keeps green

@keycloak-github-bot keycloak-github-bot Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link
Copy Markdown

Unreported flaky test detected

If 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.x509.X509BrowserLoginTest#changeLocaleOnX509InfoPage

Keycloak CI - Java Distribution IT (windows-latest - temurin - 19)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:307)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

@mhajas mhajas left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @vramik and @ahus1! The changes lgtm! I believe this is ready for merging. I will leave this to you as I am only on mobile phone and maybe I am missing something.

@ahus1
ahus1 commented Feb 16, 2024
Copy link
Copy Markdown
Member

otherwise I would request you to fix the commit message that says "Fimpmlified method with a direct return" :)

Oh, covfefe!!

@ahus1
ahus1 merged commit 7645355 into keycloak:main Feb 16, 2024
@ahus1
ahus1 commented Feb 16, 2024
Copy link
Copy Markdown
Member

Thank you for everyone who helped with this task in the discussions, ideas and iterations until we got to this point to get this merged. I hope we got it correct in all necessary the variant where attributes are used. Let's keep an eye on this in the coming weeks before the next KC24 to spot unexpected regressions.

Parts of this change wouldn't have been possible without the declarative user profile. With that in place, it was possible to have both a default length validation and a UI to configure the length of each field. Thank you!

@overheadhunter
Copy link
Copy Markdown

If I get this right, this change didn't increase the limit for group attributes though, did it?

@ahus1
ahus1 commented Mar 15, 2025
Copy link
Copy Markdown
Member

@overheadhunter - no, it didn't. Feel free to open a new issue for that if none exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

User attributes with a text more than 255 characters

9 participants

0