Keycloak registration - custom user attribute value not picked up correctly - keycloak

I am evaluating KeyCloak, and am trying to configure it as a SAML IDP to a SalesForce client. The SAML flow works fine, however I am running into an issue with new user registration. KeyCloak is backed by a User Federation LDAP (AD LDS).
I have a custom field mapped to the cn field in the LDAP, and have updated the register.ftl in my custom theme. I have also added a mapper, which maps this custom field to the LDAP attribute.
I get an error when I try to register the user, and it looks like the value of the custom attribute is not being picked up correctly.
I cannot find anything useful when I search online, which makes me think that there is probably a silly mistake somewhere that I haven't spotted.
Mapping:
Name: MRN
Mapper Type: user-attribute-ldap-mapper
User Model Attribute: mrn
LDAP Attribute: cn
Read Only: OFF
Is Mandatory In LDAP: ON
Is Binary Attribute : OFF
Federation provider settings:
Username LDAP attribute :userPrincipalName
RDN LDAP attribute :cn
UUID LDAP attribute :mail
Template:
<div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('mrn',properties.kcFormGroupErrorClass!)}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="mrn" class="${properties.kcLabelClass!}">MRN</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="user.attributes.mrn" class="${properties.kcInputClass!}" name="user.attributes.mrn" value="${(register.formData['user.attributes.mrn']!'')}" />
</div>
</div>
This is the error I get when I try to register a new user (I entered 123456 into the MRN field) :
14:08:43,514 WARN [org.keycloak.services] (default task-1) KC-SERVICES0013: Failed authentication: org.keycloak.models.ModelException: Could not retrieve identifier for entry [cn=\ ,OU=Emp,DC=MyPortal,DC=TEST,DC=COM].
at org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore.getEntryIdentifier(LDAPIdentityStore.java:546)
at org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore.add(LDAPIdentityStore.java:99)
at org.keycloak.storage.ldap.LDAPUtils.addUserToLDAP(LDAPUtils.java:78)

I am not sure how this was fixed, but I deleted my mapper and re-created it, and it is working now. The only difference I can see is that the new mapper has its name in lower case while the old one had it in all uppercase. I do not see why that should matter though..

Related

How to access OIDC client information in Keycloak E-Mail templates?

I know how to access some variable information in Keycloak E-Mail templates. E.g.:
user.getUsername()
user.getEmail()
user.getFirstName()
user.getLastName()
But I need to access client specific variables. The Keycloak Java Code shows there is all information I need in the ClientModel Java Class: https://github.com/keycloak/keycloak/blob/main/server-spi/src/main/java/org/keycloak/models/ClientModel.java
client.getClientId()
client.getName()
client.getDescription()
client.getRootUrl()
client.getBaseUrl()
client.getAttribute(name)
And the client_id=account Query Parameter is also set on the page, where the password reset action is triggered:
https://example.com/auth/realms/my-realm/login-actions/reset-credentials?client_id=account&tab_id=bQiVx012SZg
The information is set on the client:
But the client varaible seems to be unset while the email template gets rendered.
# password-reset.ftl
# This does NOT work
${client.name}
# This does NOT work
${kcSanitize(msg("clientinfohtml",client.getName()))?no_esc}
How to access client variables in Keycloak E-Mail templates?
Yep, client segment seems not accessible and give https://null !
But if you check availabilty of segment it is not empty!
<#if client?? && client['baseUrl']?? && client['baseUrl']?has_content>
<img height="100" src='${client["rootUrl"]}/${kcSanitize(msg("emailSignatureLogoUrl"))?no_esc}' style="max-width: 250px;">
<#else>
<img height="100" src='${kcSanitize(msg("emailSignatureLogoUrl"))?no_esc}' style="max-width: 250px;">
</#if>
This is the same result with client['rootUrl']
Searching an anwser, I have found this topics about Model provisioning for EmailTemplate:
https://keycloak.discourse.group/t/access-to-group-attributes-in-custom-email-template/10874/2

Add custom validation in Update User Profile in Keycloak

I have added a custom attribute in login-update-profile.ftl named organization, it is able to save the input from user into Keycloak.
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="user.attributes.organization" class="${properties.kcLabelClass!}">${msg("organization")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="user.attributes.organization" name="user.attributes.organization" value="${(user.attributes.organization!'')}" class="${properties.kcInputClass!}" aria-invalid="<#if messagesPerField.existsError('organization')>true</#if>"
/>
</div>
<#if messagesPerField.existsError('organization')>
<span id="input-error-organization" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('organization'))?no_esc}
</span>
</#if>
</div>
</div>
How to add validation for this field? I need to make it an obligatory field and meet certain condition (for example the length of the string). If the input is invalid, the error message is expected to be shown (like what we see in email or username field)
I found out the solution by creating a custom implementation of service provider Review Profile in First Broker Login flow. Here is the steps:
Open keycloak's github repo and find the relevant Java codes for Review Profile. At the time of writing this answer (keycloak v15.0.2), we need 3 files: AbstractIdpAuthenticator.java, IdpReviewProfileAuthenticatorFactory.java, and IdpReviewProfileAuthenticator.java
Make sure you are able to build a .jar using those files
Open IdpReviewProfileAuthenticator.java and add this function:
public List<FormMessage> getCustomAttributeError(String organization) {
List<FormMessage> errors = new ArrayList<>();
// You can add more conditions & parameters to be validated
if(Validation.isBlank(organization)){
errors.add(new FormMessage("organization", "missingOrganizationMessage"));
}
return errors;
}
Go to actionImpl function and add this lines between profile.update((attributeName, userModel) and catch (ValidationException pve):
if(getCustomAttributeError(profile.getAttributes().getFirstValue("organization")).size() > 0){
throw new ValidationException();
}
Add this lines in catch (ValidationException pve) after List<FormMessage> errors:
List<FormMessage> extraErrors = getCustomAttributeErrors(profile.getAttributes().getFirstValue("organization"));
for(FormMessage error : extraErrors) {
errors.add(error);
}
Open IdpReviewProfileAuthenticatorFactory.java go to getDisplayType() function and change the return value into "Custom Review Profile"
Build the .jar, deploy it into keycloak, create a copy of First Broker Login flow we name Custom First Broker Login, and in Custom First Broker Login we replace Review Profile with Custom Review Profile
Configure Custom Review Profile by clicking it's action button, give it an alias, and turn Update Profile on First Login into on
Bind the desired Identity Providers with the new Custom First Broker Login

populated attrib in form element doesn't get its value passed

I'm creating a form text field, but would like to set an additional attribute called additional so the html markup looks like this.
<dd id="email-element">
<input type="text" value="" id="email" name="email" additional="">
</dd>
I'm able to set the attribute using setAttrib like so.
$email = new Zend_Form_Element_Text('email');
$email->setAttrib('additional', '');
$this->addElement($email);
I'm then setting the value of additional on the client side via ajax. But when the form is submitted, additional appears empty. When I var_dump the form, I can see it as an attribute on this form field, but it's empty. Also when I var_dump the request, it's not on it (which is understandable since it's an attribute, and not the field value itself). Is there a way to read attributes that were changed on the client side?
PHP has no way of reading form attributes that were modified in the browser, but you can read it on the client side and send it back to PHP. The only data submitted are the element values themselves.
If you need the attribute in PHP, add a hidden input called additional (or whatever you like), and during the form's onsubmit event, you can read the value of the attribute, and populate the hidden element and then submit the form. Note that if the client has Javascript disabled, the value will not come through, but that method can be used to read it and send it to the server.
Hope that helps.

Spring #ModelAttribute doesn't care about commandName

JSP:
<form:form commandName="editWeather" method="post" action="../edit">
<!-- Input fields -->
<input type="submit" value="Submit">
</form:form>
And this is how I get the model in Spring:
#ModelAttribute("DONTGIVEADAMN") Weather weather
And I can still use the weather to do my operations and it works great, for example:
weatherService.editWeather(weather);
My question is...Why does this work?
Model attribute name doesn't matter when binding data received from a form (because names of form fields correspond to the names of fields of the model object), it matters only when rendering a form.
I particular, when model attribute name in your POST handler method doesn't match the commandName in the form, you will be able to receive the data, but won't be able to redisplay a form with validation errors.
its matching the class type (or interface), not the name of the variable/parameter; and the specified request mapping/method signature must be correct.

Why do forms timeout in ColdFusion?

We're on CF 8 and we have a CF application with a 1 hour session timeout specified.
<cfapplication sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0, 0, 60, 0)#" name="myAppName" clientmanagement="yes">
Within the CF administrator, we have a default session timeout of 1 hour, and a max session timeout of 1 day.
We're having some odd (intermittent) form timeout issues on submission of a regular form (not a flash form)...
Let's say the user hits the form page at 10:30am. (I know this because I'm appending the time to the "Action" property of the form tag). At 11:10am, the user submits the form, but none of the form struct is available to the action page, so the user gets an error.
I know that it's coming from the correct page since the CGI.referrer is defined properly.
In my custom error handler, I iterate over any form, session, or client structs -- and there is NO form struct defined at this point. All of the other Session and Client variables ARE still available.
Excerpts from Form Page:
<cfform name="chairsComment" id="chairsComment" action="library/save_chairsComment.cfm?Start=0224_153027" method="post" >
<input name="chairsCommentNumber" id="chairsCommentNumber" type="hidden" value="13" />
<textarea name="comment_13" rows="50" wrap="virtual" cols="100" id="comment_13" ></textarea>
<input name="save_answer" id="save_answer" type="submit" value="Save Response" />
</cfform>
And for the Action page, it throws an error on line 1:
<cfset whichCommentNumber = form.chairsCommentNumber>
It works during testing, but when some of our users use it, it throws the error that "Element CHAIRSCOMMENTNUMBER is undefined in FORM." We've just started looking at the specific browser that this individual is using (Safari 4.0.3) and asked him to upgrade to 5.x, but with SUCH a vanilla form, it seems an unlikely culprit.
Any Ideas?
In the midst of a discussion on Ray Camden's blog about file uploading, someone mentions a problem with Safari 4.0.5 throwing an error because the form field did not contain a file ... it's not the same problem, necessarily, but it could be related. Unfortunately, the commenter never returned with more information.
There's a post on another blog here where a commenter mentions an issue with Safari and a cfform inside a cfdiv tag. If your cfform is similarly nested, you might need to reverse the order (nest the cfdiv inside the form) to make this work.