Escape ldap id when using normal attribute syntax (#25) (#25036)

Closes https://github.com/keycloak/security/issues/46

Co-authored-by: Ricardo Martin <rmartinc@redhat.com>
This commit is contained in:
Stian Thorgersen 2023-11-27 11:38:14 +01:00 committed by GitHub
parent b1db526804
commit a32b58d337
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 2 deletions

View file

@ -24,6 +24,7 @@ import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.storage.ldap.LDAPConfig;
import org.keycloak.storage.ldap.idm.model.LDAPDn;
import org.keycloak.storage.ldap.idm.query.EscapeStrategy;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import org.keycloak.storage.ldap.idm.store.ldap.extended.PasswordModifyRequest;
import org.keycloak.storage.ldap.mappers.LDAPOperationDecorator;
@ -386,7 +387,7 @@ public class LDAPOperationManager {
).append(LDAPUtil.convertGUIDToEdirectoryHexString(id)).append(")");
} else {
filter.append("(objectClass=*)(").append(getUuidAttributeName()).append(LDAPConstants.EQUAL)
.append(id).append(")");
.append(EscapeStrategy.DEFAULT.escape(id)).append(")");
}
if (config.getCustomUserSearchFilter() != null) {

View file

@ -170,7 +170,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
private UserModel getUserFromForm(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) {
if (username == null || username.isEmpty()) {
context.getEvent().error(Errors.USER_NOT_FOUND);
Response challengeResponse = challenge(context, getDefaultChallengeMessage(context), FIELD_USERNAME);
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);

View file

@ -31,6 +31,7 @@ import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
@ -198,4 +199,45 @@ public class LDAPSpecialCharsTest extends AbstractLDAPTest {
});
}
@Test
public void test04_loginWithSpecialCharacterUsingSameUUIDThanUsernameAttribute() {
// remove users from the ldap to use the new UUID attribute
adminClient.realm(TEST_REALM_NAME).userStorage().removeImportedUsers(ldapModelId);
// change the UUID attribute to be the username attribute
String origUuidAttrName = testingClient.server().fetch(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
String uidAttrName = ctx.getLdapProvider().getLdapIdentityStore().getConfig().getUsernameLdapAttribute();
String origUuidAttrNamee = ctx.getLdapModel().get(LDAPConstants.UUID_LDAP_ATTRIBUTE);
ctx.getLdapModel().put(LDAPConstants.UUID_LDAP_ATTRIBUTE, uidAttrName);
ctx.getRealm().updateComponent(ctx.getLdapModel());
return origUuidAttrNamee;
}, String.class);
try {
// assert the user is found and UUID is the name
List<UserRepresentation> users = adminClient.realm(TEST_REALM_NAME).users().search("jamees,key*cložak)ppp", true);
Assert.assertEquals("User not found", 1, users.size());
UserRepresentation jamees = users.iterator().next();
Assert.assertEquals("Incorrect user", "jamees,key*cložak)ppp", jamees.getUsername());
Assert.assertEquals("Incorrect UUID attribute", "jamees,key*cložak)ppp", jamees.firstAttribute(LDAPConstants.LDAP_ID));
// Fail login with wildcard
loginPage.open();
loginPage.login("jamees*", "Password1");
Assert.assertEquals("Invalid username or password.", loginPage.getInputError());
// Success login as username exactly match
loginPage.login("jamees,key*cložak)ppp", "Password1");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
} finally {
// Revert config changes to be back to previous UUID attribute
ComponentRepresentation ldapRep = testRealm().components().component(ldapModelId).toRepresentation();
ldapRep.getConfig().putSingle(LDAPConstants.UUID_LDAP_ATTRIBUTE, origUuidAttrName);
testRealm().components().component(ldapModelId).update(ldapRep);
}
}
}