Hi All,
Sorry for such a long first post. Here we go!
TL;DR:
I want to look at
https://issues.jboss.org/browse/KEYCLOAK-5571 as it is impacting us. I’m
happy to contribute code or write a blog on what configuration settings are needed to
achieve this. While the SAGA has more context, here’s a few of my currently burning
questions:
1. What implements the
org.keycloak.admin.client.resource.UserResource.update(UserRepresentation) and
UserRepresentation ...toRepresentation() interface method? (from the
integration/admin-client directory - I can’t find the business logic)
2. What would be the right approach to wire up the admin ui User Enabled toggle to a LDAP
boolean field, and where in the codebase would that go? (if you can cite examples of
similar that would be great)
3. What is the best way to go about setting up an IDE for development? Just importing the
root POM into IDEA doesn’t seem to cut it.
4. If I provide a patch for this, is this something that might be considered for pulling
into master?
I am interested in all of the features within KEYCLOAK-5571, as a few other requirements,
but I’m happy to start here and treat the others as atomic suggested changes. They may
include:
* Supporting incremented default values for new users (the uidNumber must be unique and it
should be 1 greater than the highest uidNumber that the system can see … i.e. the next
available UID).
* Supporting out-of-band password recovery (where by a code is sent via a trusted path
[text message, telephone call, in person conversation with the user] which can be used to
reset their password - ideally in combination with another stored value, such as their
employee id / tax id / post code / something else which is relatively static but
relatively unknown) - this could be developed outside of Keycloak of course, but would
ideally be within the same system.
If addressing KEYCLOAK-5571 goes well, I would be interested in continuing to contribute
down these paths.
Thanks for your time, I would love to get involved … I just need a bit of help.
THE SAGA:
Apologies if this message should be in keycloak-users, and if any of it seems incoherent.
I’ve been fighting in circles all weekend and I have to admit that I’m not entirely sure
I’m approaching the problem correctly. Please bear with me as I’m not entirely sure how to
articulate things at this point, but I know I need help!
Problem statement: we are currently using OpenLDAP to manage access to our systems.
However, the administration interface is crude and it lacks SAML/OIDC support for
integrating systems like Google Suite, AWS Console, Office 365 and others. It also lacks a
self service console where users can mange their own accounts. Keycloak at first glance
looks ideal - especially as it allows us to continue using OpenLDAP as the primary source
of truth, with Keycloak used to enhance the user experience giving self service and
integration with SAML/OIDC clients.
As per the docs, some mapping is required to have OpenLDAP support the storage of Keycloak
data within the OpenLDAP schemas. Unfortunately, I’ve not bee able to find documentation
for what those fields names in Keycloak can be and how I should alter my OpenLDAP schema
to support them. I found KEYCLOAK-5571 which appears to cover at least some of the issues
I’m having. Amongst other things, I’m a Java developer, so I’m comfortable with working in
code and submitting patches. Assuming that the answer isn’t configuration, is this
something that would be valuable to contribute? If so, is there any advice that this list
can offer on where to start?
What follows is my journey as an outsider into trying to figure out things myself. This
may or may not be of interest - but given this list gets indexed by Google, it might help
someone in future. Seeing that issue (KEYCLOAK-5571) I figured the best place to start
would be the admin ui where you enable/disable users. I thought that I would start at the
browser and try to figure out what the enabled/disable user toggle did when saved, trace
that into the server side endpoint that picked up that representation and hopefully find
out where & why it didn’t make it through to LDAP.
I noticed that there was a PUT to
http://localhost/auth/admin/realms/master/users/f:$UUID:$USERNAME and as part of the JSON
payload was “enabled: false”. At this point I started grepping around in the Keycloak
code. I figured that
org.keycloak.admin.client.resource.UserResource.update(UserRepresentation) Interface was
what was being called, but unfortunately when I opened up the root POM then IDEA only saw
the files as plain text and none of the Intelisense worked, I could grep around the code
though. When I opened up the integration/admin-client/pom.xml it recognise the Java files,
however I wasn’t able to find what was implementing this. If found "public static
UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel
user)” in server-spi-private org.keycloak.models.util.ModelToRepresentation, but couldn’t
find the glue which connects them together. I’m guessing there might be some WildFly or
other magic going on which I’m not aware of?
Seeing the “enabled: false” lead me to think that I might be able to create a
user-attribute-ldap-mapper from the user model attribute “enabled” to an “enabled” LDAP
attribute I added to our schema to test. The LDAP attribute has SYNTAX
1.3.6.1.4.1.1466.115.121.1.7 (a binary attribute) and I’ve checked I can set that to TRUE
/ FALSE appropriately. I set it to be mandatory in LDAP and set it to be a Binary
Attribute - however when I save it says "Error! With Binary attribute enabled, the
'Always read value from LDAP' must be enabled too” - however there is no “Always
read value from LDAP” option! However, after enabling Import Users in the LDAP user
federation settings, “Always read value from LDAP” becomes available. It’s not clear if
Binary Attributes are supported only in this configuration, but ideally I would like to
not Import Users as I prefer LDAP to be the authoritative source. After this I can
disabled Import Users and the configuration still seemingly remains valid without any
errors in the logs. That said, it’s not erroring about not properly persisting the enabled
state to LDAP...
If I go across to the user in the admin ui, even though enabled is set to FALSE in LDAP,
the toggle is showing as enabled in the UI. The JSON it gets for the UserRepresentation on
the client side is “enabled”:true which explains the state of the toggle. If I stick
Wireshark locally, setup a Docker with OpenLDAP and configure it appropriately, sniffing
traffic I can see that the enabled attribute for my user comes back as FALSE. So there is
something going wrong when trying to build that UserRepresentation. I suspect at the root
of the KEYCLOAK-5571 issue. If I change the toggle to false in the UI and save, then
reload the page, the toggle is back to true - when it persists to the LDAP server, it’s
sending enabled: FALSE - this doesn’t make sense, but it might be just repeating back to
LDAP what it read in without changing that field. If I change the name as well, it does
send those fields updated, but enabled remains FALSE in the LDAP server.
Given that I didnt get very far with the UserRepresentation angle, I thought about going
down the FederatedStorage - something must map the model into LDAP, as changes to the
first name / last name, and the other attributes seem to be persisted and loaded in LDAP
just fine. In my grepping around in server-spi I found a org.keycloak.models.UserModel
Interface, which had a org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage
implementation with a ENABLED_ATTRIBUTE = “ENABLED” field and isEnabled / setEnabled
methods which getFirstAttribute(ENABLED_ATTRIBUTE) / setSingleAttribute(ENABLED_ATTRIBUTE,
Boolean.toString(enabled). The class comment has:
* Assumes everything is managed by federated storage except for username. getId() returns
a default value
* of "f:" + providerId + ":" + getUsername(). UserModel properties
like enabled, firstName, lastName, email, etc. are all
* stored as attributes in federated storage.
I’m not sure how the case difference between “enabled” in the UserModel properties and
“ENABLED” as listed in the class field is connected - but there must be a mapping
somewhere, as “firstName” is similarly “FIRST_NAME” and that maps just fine. I found
model/jpa contained org.keycloak.models.jpa.entities/UserEntity which had @Column(name =
"ENABLED”) protected boolean enabled, perhaps this is the link and even with Import
Users disabled it always goes through the database?
I’ve yet to find the trigger which calls the mapper to run which persists into the
database. Part of the problem is that I’m acutely aware my IDE is not setup to effectively
jump around the code base, or to effectively attach my IDE as a debugger so I can add
breakpoints and step through the code to figure out what happens where. I’ve just turned
on trace logging - but this is giving me a wall of text which may take sometime to
process. I’ve also yet to comb through the H2 DB to see if there’s cause there.
Any assistance on this would be most welcome.
ON ANOTHER NOTE:
I checked out the code and ran the build as documented against Java 8 on my mac, but
unfortunately it failed. I ignored it and progressed, but here’s some excerpts from the
output:
[INFO] Keycloak Integration TestSuite - deprecated ........ FAILURE [07:04 min]
[ERROR] Errors:
[ERROR] OIDCKeyCloakServerBrokerBasicTest.testLogoutWorksWithTokenTimeout:131 »
Processing
[ERROR] OIDCKeycloakServerBrokerWithConsentTest.before:84 » Processing
java.lang.NoSuc...
[ERROR] BrokenUserStorageTest.testBootWithBadProviderId:118 » Processing
java.lang.NoS...
[ERROR] JaxrsBasicAuthTest.testBasic:120 » NoSuchMethod
org.apache.commons.io.output.D...
[ERROR] JaxrsFilterTest.testBasic:129 » NoSuchMethod
org.apache.commons.io.output.Defe...
[ERROR] Tests run: 238, Failures: 0, Errors: 5, Skipped: 32
I guess this shouldn’t happen on a fresh check out & a following of the instruction.
If you made it this far - bravo!
Thanks again,
—
Dan Hardiker | Adaptavist