KEYCLOAK-19056 EDIT MODE field should not be leave empty (#8380)

This commit is contained in:
Marek Posolda 2021-09-14 20:27:09 +02:00 committed by GitHub
parent 6d0708d263
commit 11e5f66c60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 62 additions and 9 deletions

View file

@ -654,8 +654,8 @@ public class LDAPStorageProvider implements UserStorageProvider,
String password = input.getChallengeResponse(); String password = input.getChallengeResponse();
LDAPObject ldapUser = loadAndValidateUser(realm, user); LDAPObject ldapUser = loadAndValidateUser(realm, user);
if (ldapIdentityStore.getConfig().isValidatePasswordPolicy()) { if (ldapIdentityStore.getConfig().isValidatePasswordPolicy()) {
PolicyError error = session.getProvider(PasswordPolicyManagerProvider.class).validate(realm, user, password); PolicyError error = session.getProvider(PasswordPolicyManagerProvider.class).validate(realm, user, password);
if (error != null) throw new ModelException(error.getMessage(), error.getParameters()); if (error != null) throw new ModelException(error.getMessage(), error.getParameters());
} }
try { try {
LDAPOperationDecorator operationDecorator = null; LDAPOperationDecorator operationDecorator = null;

View file

@ -281,6 +281,18 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory<LD
throw new ComponentValidationException("ldapErrorCantEnableStartTlsAndConnectionPooling"); throw new ComponentValidationException("ldapErrorCantEnableStartTlsAndConnectionPooling");
} }
// editMode is mandatory
if (config.get(LDAPConstants.EDIT_MODE) == null) {
throw new ComponentValidationException("ldapErrorEditModeMandatory");
}
// validatePasswordPolicy applicable only for WRITABLE mode
if (cfg.getEditMode() != UserStorageProvider.EditMode.WRITABLE) {
if (cfg.isValidatePasswordPolicy()) {
throw new ComponentValidationException("ldapErrorValidatePasswordPolicyAvailableForWritableOnly");
}
}
if (!userStorageModel.isImportEnabled() && cfg.getEditMode() == UserStorageProvider.EditMode.UNSYNCED) { if (!userStorageModel.isImportEnabled() && cfg.getEditMode() == UserStorageProvider.EditMode.UNSYNCED) {
throw new ComponentValidationException("ldapErrorCantEnableUnsyncedAndImportOff"); throw new ComponentValidationException("ldapErrorCantEnableUnsyncedAndImportOff");
} }

View file

@ -233,7 +233,7 @@ public class UserStorageRestTest extends AbstractAdminTest {
@Test @Test
public void testValidateAndCreateLdapProvider() { public void testValidateAndCreateLdapProviderCustomSearchFilter() {
// Invalid filter // Invalid filter
ComponentRepresentation ldapRep = createBasicLDAPProviderRep(); ComponentRepresentation ldapRep = createBasicLDAPProviderRep();
@ -271,6 +271,7 @@ public class UserStorageRestTest extends AbstractAdminTest {
ldapRep2.setProviderType(UserStorageProvider.class.getName()); ldapRep2.setProviderType(UserStorageProvider.class.getName());
ldapRep2.setConfig(new MultivaluedHashMap<>()); ldapRep2.setConfig(new MultivaluedHashMap<>());
ldapRep2.getConfig().putSingle("priority", Integer.toString(2)); ldapRep2.getConfig().putSingle("priority", Integer.toString(2));
ldapRep2.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.UNSYNCED.name());
ldapRep2.getConfig().putSingle(LDAPConstants.BIND_DN, "cn=manager"); ldapRep2.getConfig().putSingle(LDAPConstants.BIND_DN, "cn=manager");
ldapRep2.getConfig().putSingle(LDAPConstants.BIND_CREDENTIAL, "password"); ldapRep2.getConfig().putSingle(LDAPConstants.BIND_CREDENTIAL, "password");
String id2 = createComponent(ldapRep2); String id2 = createComponent(ldapRep2);
@ -284,6 +285,40 @@ public class UserStorageRestTest extends AbstractAdminTest {
removeComponent(id2); removeComponent(id2);
} }
@Test
public void testValidateAndCreateLdapProviderEditMode() {
// Test provider without editMode should fail
ComponentRepresentation ldapRep = createBasicLDAPProviderRep();
ldapRep.getConfig().remove(LDAPConstants.EDIT_MODE);
Response resp = realm.components().add(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Test provider with READ_ONLY edit mode and validatePasswordPolicy will fail
ldapRep = createBasicLDAPProviderRep();
ldapRep.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.name());
ldapRep.getConfig().putSingle(LDAPConstants.VALIDATE_PASSWORD_POLICY, "true");
resp = realm.components().add(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Test provider with UNSYNCED edit mode and validatePasswordPolicy will fail
ldapRep.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.UNSYNCED.name());
ldapRep.getConfig().putSingle(LDAPConstants.VALIDATE_PASSWORD_POLICY, "true");
resp = realm.components().add(ldapRep);
Assert.assertEquals(400, resp.getStatus());
resp.close();
// Test provider with WRITABLE edit mode and validatePasswordPolicy will fail
ldapRep.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.name());
ldapRep.getConfig().putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true");
String id1 = createComponent(ldapRep);
// Cleanup
removeComponent(id1);
}
@Test @Test
public void testUpdateProvider() { public void testUpdateProvider() {
ComponentRepresentation ldapRep = createBasicLDAPProviderRep(); ComponentRepresentation ldapRep = createBasicLDAPProviderRep();
@ -392,6 +427,7 @@ public class UserStorageRestTest extends AbstractAdminTest {
ldapRep.setProviderType(UserStorageProvider.class.getName()); ldapRep.setProviderType(UserStorageProvider.class.getName());
ldapRep.setConfig(new MultivaluedHashMap<>()); ldapRep.setConfig(new MultivaluedHashMap<>());
ldapRep.getConfig().putSingle("priority", Integer.toString(2)); ldapRep.getConfig().putSingle("priority", Integer.toString(2));
ldapRep.getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.name());
return ldapRep; return ldapRep;
} }

View file

@ -56,7 +56,8 @@
"providerName": "ldap", "providerName": "ldap",
"priority": 1, "priority": 1,
"config": { "config": {
"connectionUrl": "ldap://foo" "connectionUrl": "ldap://foo",
"editMode": "WRITABLE"
} }
}, },
{ {
@ -64,7 +65,8 @@
"providerName": "ldap", "providerName": "ldap",
"priority": 2, "priority": 2,
"config": { "config": {
"connectionUrl": "ldap://bar" "connectionUrl": "ldap://bar",
"editMode": "WRITABLE"
} }
} }
], ],

View file

@ -1184,7 +1184,7 @@ ldap.edit-mode.tooltip=READ_ONLY is a read-only LDAP store. WRITABLE means data
update-profile-first-login=Update Profile First Login update-profile-first-login=Update Profile First Login
update-profile-first-login.tooltip=Update profile on first login update-profile-first-login.tooltip=Update profile on first login
sync-registrations=Sync Registrations sync-registrations=Sync Registrations
ldap.sync-registrations.tooltip=Should newly created users be created within LDAP store? Priority effects which provider is chosen to sync the new user. ldap.sync-registrations.tooltip=Should newly created users be created within LDAP store? Priority effects which provider is chosen to sync the new user. This setting is effectively appplied only with WRITABLE edit mode.
import-enabled=Import Users import-enabled=Import Users
ldap.import-enabled.tooltip=If true, LDAP users will be imported into Keycloak DB and synced by the configured sync policies. ldap.import-enabled.tooltip=If true, LDAP users will be imported into Keycloak DB and synced by the configured sync policies.
vendor=Vendor vendor=Vendor
@ -1247,7 +1247,7 @@ ldap-connection-timeout=Connection Timeout
ldap.connection-timeout.tooltip=LDAP Connection Timeout in milliseconds ldap.connection-timeout.tooltip=LDAP Connection Timeout in milliseconds
ldap-read-timeout=Read Timeout ldap-read-timeout=Read Timeout
ldap.read-timeout.tooltip=LDAP Read Timeout in milliseconds. This timeout applies for LDAP read operations ldap.read-timeout.tooltip=LDAP Read Timeout in milliseconds. This timeout applies for LDAP read operations
ldap.validate-password-policy.tooltip=Determines if Keycloak should validate the password with the realm password policy before updating it ldap.validate-password-policy.tooltip=Determines if Keycloak should validate the password with the realm password policy before updating the LDAP mapped user. When this is false, Keycloak password policy would not be applied, which means that password will be updated on LDAP server unless LDAP server itself has some password policy rules. This setting is possible only with WRITABLE edit mode.
ldap.connection-pooling.tooltip=Determines if Keycloak should use connection pooling for accessing LDAP server ldap.connection-pooling.tooltip=Determines if Keycloak should use connection pooling for accessing LDAP server
ldap.connection-pooling.authentication.tooltip=A list of space-separated authentication types of connections that may be pooled. Valid types are "none", "simple", and "DIGEST-MD5". ldap.connection-pooling.authentication.tooltip=A list of space-separated authentication types of connections that may be pooled. Valid types are "none", "simple", and "DIGEST-MD5".
ldap.connection-pooling.debug.tooltip=A string that indicates the level of debug output to produce. Valid values are "fine" (trace connection creation and removal) and "all" (all debugging information). ldap.connection-pooling.debug.tooltip=A string that indicates the level of debug output to produce. Valid values are "fine" (trace connection creation and removal) and "all" (all debugging information).

View file

@ -11,6 +11,7 @@ invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last
invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted. invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted.
invalidPasswordGenericMessage=Invalid password: new password does not match password policies. invalidPasswordGenericMessage=Invalid password: new password does not match password policies.
ldapErrorEditModeMandatory=Edit Mode is mandatory
ldapErrorInvalidCustomFilter=Custom configured LDAP filter does not start with "(" or does not end with ")". ldapErrorInvalidCustomFilter=Custom configured LDAP filter does not start with "(" or does not end with ")".
ldapErrorConnectionTimeoutNotNumber=Connection Timeout must be a number ldapErrorConnectionTimeoutNotNumber=Connection Timeout must be a number
ldapErrorReadTimeoutNotNumber=Read Timeout must be a number ldapErrorReadTimeoutNotNumber=Read Timeout must be a number
@ -21,6 +22,7 @@ ldapErrorCantWriteOnlyAndReadOnly=Can not set write-only and read-only together
ldapErrorCantEnableStartTlsAndConnectionPooling=Can not enable both StartTLS and connection pooling. ldapErrorCantEnableStartTlsAndConnectionPooling=Can not enable both StartTLS and connection pooling.
ldapErrorCantEnableUnsyncedAndImportOff=Can not disable Importing users when LDAP provider mode is UNSYNCED ldapErrorCantEnableUnsyncedAndImportOff=Can not disable Importing users when LDAP provider mode is UNSYNCED
ldapErrorMissingGroupsPathGroup=Groups path group does not exist - please create the group on specified path first ldapErrorMissingGroupsPathGroup=Groups path group does not exist - please create the group on specified path first
ldapErrorValidatePasswordPolicyAvailableForWritableOnly=Validate Password Policy is applicable only with WRITABLE edit mode
clientRedirectURIsFragmentError=Redirect URIs must not contain an URI fragment clientRedirectURIsFragmentError=Redirect URIs must not contain an URI fragment
clientRootURLFragmentError=Root URL must not contain an URL fragment clientRootURLFragmentError=Root URL must not contain an URL fragment

View file

@ -46,11 +46,12 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="editMode">{{:: 'edit-mode' | translate}}</label> <label class="col-md-2 control-label" for="editMode"><span class="required">*</span> {{:: 'edit-mode' | translate}}</label>
<div class="col-md-6"> <div class="col-md-6">
<div> <div>
<select class="form-control" id="editMode" <select class="form-control" id="editMode"
ng-model="instance.config['editMode'][0]"> ng-model="instance.config['editMode'][0]"
required>
<option>READ_ONLY</option> <option>READ_ONLY</option>
<option>WRITABLE</option> <option>WRITABLE</option>
<option>UNSYNCED</option> <option>UNSYNCED</option>