KNOX-3330: Refactor LDAP Proxy configuration to support multiple backends#1240
KNOX-3330: Refactor LDAP Proxy configuration to support multiple backends#1240handavid wants to merge 3 commits into
Conversation
Test Results21 tests 21 ✅ 1s ⏱️ Results for commit 33fe1fe. |
…ends Gateway server configurations are updated to use 'gateway.ldap.interceptor.*' instead of 'gateway.ldap.backend.*' to allow specifying multiple types of interceptors as well as multiple backends to the LDAP proxy. BackendFactory has been modified to use the java ServiceLoader to load a factory for a backend class instead of a backend instance directly. This allows multiple backends of the same class to be configured. InterceptorFactory has been implemented following the same pattern. GroupLookupInterceptor is renamed to UserSearchInterceptor to more accurately describe what it does. Multiple UserSearchInterceptors can be configured with each forwarding the search to its backend and appending the results. A DuplicateUserFilteringInterceptor has been implemented that will filter out search Entries with the same UID that are returned from different backends.
33fe1fe to
dd9de8d
Compare
|
rebased and fixed conflicts |
smolnar82
left a comment
There was a problem hiding this comment.
I'll keep the review later today.
There was a problem hiding this comment.
Please remove this file from the commit (and the other .jceks files too).
| String[] namesArray = names.split(","); | ||
| List<String> namesList = new ArrayList<>(); | ||
| for (String name : namesArray) { | ||
| if (!Strings.isNullOrEmpty(name)) { |
There was a problem hiding this comment.
We use org.apache.commons.lang3.StringUtils#isNotBlank in the project for the same purpose.
This is already imported -> the new import will be eliminated too.
Even better, we already have a method to get comma separated config values as list: org.apache.knox.gateway.config.impl.GatewayConfigImpl#splitConfigValueToList
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| public class DuplicateUserFilteringInterceptor extends BaseInterceptor { |
There was a problem hiding this comment.
Please add unit test coverage for this class.
| while (originalResults.next()) { | ||
| originalEntries.add(originalResults.get()); | ||
| } | ||
| originalResults.close(); |
There was a problem hiding this comment.
Please ensure that originalResults.close() is always invoked to avoid cursor leaks (using try-with-resources is a good option here).
E.g.
try (EntryFilteringCursor originalResults = next(ctx)) {
...
}
| public class DuplicateUserFilteringInterceptorFactory implements KnoxLdapInterceptorFactory { | ||
| @Override | ||
| public Interceptor create(String name, Map<String, String> config) { | ||
| // NOTE: no further configuration expected for this Interceptor |
There was a problem hiding this comment.
nit: I think this JavaDoc comment is redundant and can be obsolete quickly.
|
|
||
| @Override | ||
| public String getType() { | ||
| return "duplicateuserfilter"; |
There was a problem hiding this comment.
nit: using a constant would be better
| // if no results, perform single-user search | ||
| if (entries.isEmpty()) { | ||
| // Specific user lookup | ||
| LOG.ldapUserLoaded(username); |
There was a problem hiding this comment.
This log line should be under the actual getUser(username); line
smolnar82
left a comment
There was a problem hiding this comment.
Another batch of comments.
I'll review the tests tomorrow.
| List<Entry> entries = new ArrayList<>(); | ||
| entries.addAll(backend.searchUsers(userSearchString, schemaManager)); | ||
| return entries; |
There was a problem hiding this comment.
Why not simply: return backend.searchUsers(userSearchString, schemaManager)?
In fact, if this is a simple inline backend invocation, why we have this private method at all? No any particular check, logic, etc...in here.
| } | ||
|
|
||
| private Entry getUser(String username) throws Exception { | ||
| return backend.getUser(username, schemaManager); |
There was a problem hiding this comment.
The same as above: I don't think this private method is necessary.
| DirectoryService directoryService; | ||
| private LdapServer ldapServer; | ||
| private LdapBackend backend; | ||
| private List<Interceptor> interceptors = new ArrayList<>(); |
There was a problem hiding this comment.
This is redundant, as you initialize interceptors anyway (i.e. not simply add entries into it).
See new line 80: this.interceptors = createInterceptors(config)
|
|
||
| @Override | ||
| public String getType() { | ||
| return "backend"; |
There was a problem hiding this comment.
nit: Should be a constant (we could reuse it in KnoxLdapManager, see my comment about not using the instanceof operator below).
| // Add our configured interceptors | ||
| SchemaManager schemaManager = directoryService.getSchemaManager(); | ||
| for (Interceptor interceptor : interceptors) { | ||
| if (interceptor instanceof UserSearchInterceptor) { |
There was a problem hiding this comment.
I personally am not a big fan of the instanceof operator, so this feel free to ignore this comment :)
It might be an option to store the interceptors above in a Map<String, Interceptor>, where the key is the interceptor type. If that was the case, we could simply check if the Map.Entry.key.equals("backend") (this is where my comment about creating the backend interceptor type constant would be useful).
| if (!baseDns.contains(remoteBaseDn)) { | ||
| //create partition | ||
| String id = backend.getName().replaceAll("\\s+", ""); | ||
| JdbmPartition remotePartition = new JdbmPartition(schemaManager, directoryService.getDnFactory()); | ||
| remotePartition.setId(id); | ||
| remotePartition.setSuffixDn(new Dn(schemaManager, remoteBaseDn)); | ||
| remotePartition.setPartitionPath(new File(workDir, id).toURI()); | ||
| directoryService.addPartition(remotePartition); | ||
| baseDns.add(remoteBaseDn); | ||
| } |
There was a problem hiding this comment.
nit: a new private method (addRemotePartition, for instance) would increase the readability of this part of the code.
| <property> | ||
| <name>gateway.ldap.backend.file.dataFile</name> | ||
| <name>gateway.ldap.interceptor.ldapfile.backendType</name> | ||
| <value>ldap</value> |
| <!-- LDAP proxy backend configuration (gateway.ldap.interceptor.<interceptorName>.backendType=ldap) --> | ||
| <!-- This backend proxies to an external LDAP server (e.g., demo LDAP) --> | ||
| <!-- | ||
| Example 1: Using Knox demo LDAP server (default port 33389) |
There was a problem hiding this comment.
I'm not sure I like the idea of putting all these samples in the gateway-site.xml here.
We should rather create the new section in our user guide (see the knox-site module), and add all these sample configs there.
KNOX-3330 - Refactor Knox LDAP Proxy configuration and implementation to allow multiple backends to be simultaneously configured
What changes were proposed in this pull request?
Gateway server configurations are updated to use 'gateway.ldap.interceptor.' instead of 'gateway.ldap.backend.' to allow specifying multiple types of interceptors as well as multiple backends to the LDAP proxy.
BackendFactory has been modified to use the java ServiceLoader to load a factory for a backend class instead of a backend instance directly. This allows multiple backends of the same class to be configured. InterceptorFactory has been implemented following the same pattern.
GroupLookupInterceptor is renamed to UserSearchInterceptor to more accurately describe what it does. Multiple UserSearchInterceptors can be configured with each forwarding the search to its backend and appending the results.
A DuplicateUserFilteringInterceptor has been implemented that will filter out search Entries with the same UID that are returned from different backends.
How was this patch tested?
Unit tests were updated.
Changes were manually tested against the test ldap server and an AD that I have access to.
The following configuration was added to the gateway-site.xml
Integration Tests
No integration test changes. PR can be updated after #1236 is merged
UI changes
no UI changes