Replies: 5 comments 8 replies
-
I like the solution Interesting aspect is that the username, first / last name and email are searchable fields. The question of treating them as first-class entity members vs attributes then has to consider the following facts:
Q: Would this change represent a performance or other issue for the storages? |
Beta Was this translation helpful? Give feedback.
-
I could see that username and email are first class citizens when we need to have special semantics on these attributes, like: special handling that a username can't be changed without checking it is unique, or changing an email address would require re-validation. Putting everything into generic attributes would result in meta-programming, and checking attribute names with string comparisons. To me, using attribute names is using a weakly typed model. It would allow a more generic approach to things, but would be more difficult to maintain IMHO. Making something accessible as an attribute and as a first class method at the same time is the worst of two worlds. It should be either-or. I'll leave the decision about "generic attributes" vs. "semantic model" to those with more experience in Keycloak, still I would lean towards "semantic model". At the same time, I'd argue not to allow both methods for the same value as it will be confusing. |
Beta Was this translation helpful? Give feedback.
-
+1 for getting rid of two ways of achieving the same thing. looking from jpa point of view ... Currently there are methods which uses exact search by When both fields would be stored as first class citizens (and assuming index is present) the query plan could use index scan and therefore the execution time would be low (in my testing environment 3.482 ms with ~10M entries). When both fields are stored as attributes (also index on attributes present) the query plan looks like
It seems it might be beneficial to store at least The other fields are used by |
Beta Was this translation helpful? Give feedback.
-
Thinking about this a bit more: On the logical level, the fields need to be accessible also as attributes: they are treated as such in user profile code (see #7080). At the same time, often accessed attributes deserve a support in model being visible as separate methods, so this seems to converge to approach On the physical level, it is desirable to leverage constraints supported by the datastore for uniqueness checks. In case of RDBMS, there should ideally be a separate column for In total - the adapters should support the getter methods for the Technical side note: Postgres, CockroachDB, MSSQL, Oracle support partial unique indices which could be used for DB-level data integrity check. AFAIK, there is no such support in MySQL / MariaDB. This could be used for e.g. uniqueness checks for the telephone number @sschu has mentioned above. |
Beta Was this translation helpful? Give feedback.
-
Introduction to the issue
Currently, there are two ways how to set some chosen attributes for UserModel (namely username, email, firstName, and lastName):
userModel.setUsername
/Email
/FirstName
/LastName
(value
)userModel,setAttribute("username"
/"email"
/"firstName"
/"lastName", value)
I am not sure how it was before (I guess it was number 1), but currently, when a new user is created approach 2 is used. See: https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java#L157
Problem statement
This change (from approach 1 to 2) doesn't cause any problems when legacy JPA adapter or new storage MapUserAdapter is used, however, it may cause problems with custom user storage providers.
An example of a wrong implementation can be found in our quickstarts where we are not checking the attribute when obtaining email https://github.com/keycloak/keycloak-quickstarts/blob/5719ba5b3df671df8e69da12632f3c47f1ac7421/user-storage-jpa/src/main/java/org/keycloak/quickstart/storage/user/UserAdapter.java#L65-L68
In some cases, this becomes really a mess where we sometimes use approach 1 and sometimes 2. This resulted, for example, in this issue: https://issues.redhat.com/browse/KEYCLOAK-19610 where the reporter uses something similar to our quickstart.
In this ^^ case, the email is present only when the user is obtained from the cache because the cached adapter is adding the functionality of checking attributes instead of just looking at the email.
Solution
#1
This issue is mainly caused by the wrong
UserAdapter
implementation in the custom storage provider. However, we don't have any documentation and we have even the quickstart made wrong, so people cannot know that approaches 1 and 2 need to be equivalent. Hence the solution for this would be the addition of documentation toUserAdapter
stating that the methods should work in a way thatuserModel.setUsername == userModel.setAttribute("username",...)
Solution
#2
Refactoring the logical layer and allowing only one of the two ways for setting these attributes. Currently, it would probably make sense to allow only 2.
Solution
#2.5
Take
#2
even more seriously and move this to the storage layer as well. We could remove the email/username/lastName/firstName fields from the entities and leave this decision to each storage implementation whether it makes sense to have this stored separately.WDYT?
Beta Was this translation helpful? Give feedback.
All reactions