Skip to content

Commit

Permalink
Extract LdapClient to plugin-toolkit
Browse files Browse the repository at this point in the history
  • Loading branch information
Praveen2112 committed Apr 28, 2022
1 parent 02955a1 commit 9e0b2e5
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 68 deletions.
5 changes: 5 additions & 0 deletions lib/trino-plugin-toolkit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@
<artifactId>joda-time</artifactId>
</dependency>

<dependency>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-annotations</artifactId>
</dependency>

<dependency>
<groupId>org.weakref</groupId>
<artifactId>jmxutils</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.password.jndi;
package io.trino.plugin.base.jndi;

import org.gaul.modernizer_maven_annotations.SuppressModernizer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.password.ldap;
package io.trino.plugin.base.ldap;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.airlift.log.Logger;
import io.airlift.units.Duration;
import io.trino.plugin.base.ssl.SslUtils;
Expand All @@ -34,9 +33,8 @@
import java.security.GeneralSecurityException;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static io.trino.plugin.password.jndi.JndiUtils.createDirContext;
import static io.trino.plugin.base.jndi.JndiUtils.createDirContext;
import static java.util.Objects.requireNonNull;
import static javax.naming.Context.INITIAL_CONTEXT_FACTORY;
import static javax.naming.Context.PROVIDER_URL;
Expand All @@ -45,16 +43,16 @@
import static javax.naming.Context.SECURITY_CREDENTIALS;
import static javax.naming.Context.SECURITY_PRINCIPAL;

public class JdkLdapAuthenticatorClient
implements LdapAuthenticatorClient
public class JdkLdapClient
implements LdapClient
{
private static final Logger log = Logger.get(JdkLdapAuthenticatorClient.class);
private static final Logger log = Logger.get(JdkLdapClient.class);

private final Map<String, String> basicEnvironment;
private final Optional<SSLContext> sslContext;

@Inject
public JdkLdapAuthenticatorClient(LdapClientConfig ldapConfig)
public JdkLdapClient(LdapClientConfig ldapConfig)
{
String ldapUrl = requireNonNull(ldapConfig.getLdapUrl(), "ldapUrl is null");
if (ldapUrl.startsWith("ldap://")) {
Expand Down Expand Up @@ -87,42 +85,22 @@ public JdkLdapAuthenticatorClient(LdapClientConfig ldapConfig)
}

@Override
public void validatePassword(String userDistinguishedName, String password)
public <T> T executeLdapQuery(String userName, String password, LdapQuery ldapQuery, LdapSearchResultProcessor<T> resultProcessor)
throws NamingException
{
createUserDirContext(userDistinguishedName, password).close();
}

@Override
public boolean isGroupMember(String searchBase, String groupSearch, String contextUserDistinguishedName, String contextPassword)
throws NamingException
{
try (CloseableContext context = createUserDirContext(contextUserDistinguishedName, contextPassword);
CloseableSearchResults search = searchContext(searchBase, groupSearch, context)) {
return search.hasMore();
try (CloseableContext context = createUserDirContext(userName, password);
CloseableSearchResults search = searchContext(ldapQuery, context)) {
return resultProcessor.process(search.searchResults);
}
}

@Override
public Set<String> lookupUserDistinguishedNames(String searchBase, String searchFilter, String contextUserDistinguishedName, String contextPassword)
throws NamingException
{
try (CloseableContext context = createUserDirContext(contextUserDistinguishedName, contextPassword);
CloseableSearchResults search = searchContext(searchBase, searchFilter, context)) {
ImmutableSet.Builder<String> distinguishedNames = ImmutableSet.builder();
while (search.hasMore()) {
distinguishedNames.add(search.next().getNameInNamespace());
}
return distinguishedNames.build();
}
}

private static CloseableSearchResults searchContext(String searchBase, String searchFilter, CloseableContext context)
private static CloseableSearchResults searchContext(LdapQuery ldapQuery, CloseableContext context)
throws NamingException
{
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
return new CloseableSearchResults(context.search(searchBase, searchFilter, searchControls));
searchControls.setReturningAttributes(ldapQuery.getAttributes());
return new CloseableSearchResults(context.search(ldapQuery.getSearchBase(), ldapQuery.getSearchFilter(), searchControls));
}

private CloseableContext createUserDirContext(String userDistinguishedName, String password)
Expand Down Expand Up @@ -211,16 +189,10 @@ public CloseableSearchResults(NamingEnumeration<SearchResult> searchResults)
this.searchResults = requireNonNull(searchResults, "searchResults is null");
}

public SearchResult next()
throws NamingException
{
return searchResults.next();
}

public boolean hasMore()
public NamingEnumeration<SearchResult> getSearchResult()
throws NamingException
{
return searchResults.hasMore();
return searchResults;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.base.ldap;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchResult;

public interface LdapClient
{
<T> T executeLdapQuery(String userName, String password, LdapQuery ldapQuery, LdapSearchResultProcessor<T> resultProcessor)
throws NamingException;

interface LdapSearchResultProcessor<T>
{
T process(NamingEnumeration<SearchResult> searchResults)
throws NamingException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.password.ldap;
package io.trino.plugin.base.ldap;

import io.airlift.configuration.Config;
import io.airlift.configuration.ConfigDescription;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.base.ldap;

import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Scopes;

import static io.airlift.configuration.ConfigBinder.configBinder;

public class LdapClientModule
implements Module
{
@Override
public void configure(Binder binder)
{
configBinder(binder).bindConfig(LdapClientConfig.class);
binder.bind(LdapClient.class).to(JdkLdapClient.class).in(Scopes.SINGLETON);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.base.ldap;

import java.util.Arrays;

import static java.util.Objects.requireNonNull;

public class LdapQuery
{
private final String searchBase;
private final String searchFilter;
private final String[] attributes;

private LdapQuery(String searchBase, String searchFilter, String[] attributes)
{
this.searchBase = requireNonNull(searchBase, "searchBase is null");
this.searchFilter = requireNonNull(searchFilter, "searchFilter is null");
requireNonNull(attributes, "attributes is null");
this.attributes = Arrays.copyOf(attributes, attributes.length);
}

public String getSearchBase()
{
return searchBase;
}

public String getSearchFilter()
{
return searchFilter;
}

public String[] getAttributes()
{
return attributes;
}

public static class LdapQueryBuilder
{
private String searchBase;
private String searchFilter;
private String[] attributes = new String[0];

public LdapQueryBuilder withSearchBase(String searchBase)
{
this.searchBase = requireNonNull(searchBase, "searchBase is null");
return this;
}

public LdapQueryBuilder withSearchFilter(String searchFilter)
{
this.searchFilter = requireNonNull(searchFilter, "searchFilter is null");
return this;
}

public LdapQueryBuilder withAttributes(String... attributes)
{
this.attributes = requireNonNull(attributes, "attributes is null");
return this;
}

public LdapQuery build()
{
return new LdapQuery(searchBase, searchFilter, attributes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.password.ldap;
package io.trino.plugin.base.ldap;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.plugin.password.ldap;
package io.trino.plugin.base.ldap;

import com.google.common.collect.ImmutableMap;
import io.airlift.units.Duration;
Expand Down
5 changes: 0 additions & 5 deletions plugin/trino-password-authenticators/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@
<artifactId>validation-api</artifactId>
</dependency>

<dependency>
<groupId>org.gaul</groupId>
<artifactId>modernizer-maven-annotations</artifactId>
</dependency>

<!-- Trino SPI -->
<dependency>
<groupId>io.trino</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ private String lookupUserDistinguishedName(String user)
String searchBase = userBaseDistinguishedName.orElseThrow();
String searchFilter = replaceUser(groupAuthorizationSearchPattern.orElseThrow(), user);
Set<String> userDistinguishedNames = client.lookupUserDistinguishedNames(searchBase, searchFilter, bindDistinguishedName.orElseThrow(), bindPassword.orElseThrow());

if (userDistinguishedNames.isEmpty()) {
String message = format("User [%s] not a member of an authorized group", user);
log.debug("%s", message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,69 @@
*/
package io.trino.plugin.password.ldap;

import com.google.common.collect.ImmutableSet;
import io.trino.plugin.base.ldap.LdapClient;
import io.trino.plugin.base.ldap.LdapQuery;

import javax.inject.Inject;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;

import java.util.Set;

public interface LdapAuthenticatorClient
import static java.util.Objects.requireNonNull;

public class LdapAuthenticatorClient
{
void validatePassword(String userDistinguishedName, String password)
throws NamingException;
private final LdapClient ldapClient;

@Inject
public LdapAuthenticatorClient(LdapClient ldapClient)
{
this.ldapClient = requireNonNull(ldapClient, "ldapClient is null");
}

public void validatePassword(String userDistinguishedName, String password)
throws NamingException
{
ldapClient.executeLdapQuery(
userDistinguishedName,
password,
new LdapQuery.LdapQueryBuilder()
.withSearchBase(userDistinguishedName)
.withSearchFilter(userDistinguishedName)
.build(),
searchResults -> null);
}

boolean isGroupMember(String searchBase, String groupSearch, String contextUserDistinguishedName, String contextPassword)
throws NamingException;
public boolean isGroupMember(String searchBase, String groupSearch, String contextUserDistinguishedName, String contextPassword)
throws NamingException
{
return ldapClient.executeLdapQuery(
contextUserDistinguishedName,
contextPassword,
new LdapQuery.LdapQueryBuilder()
.withSearchBase(searchBase)
.withSearchFilter(groupSearch).build(),
NamingEnumeration::hasMore);
}

Set<String> lookupUserDistinguishedNames(String searchBase, String searchFilter, String contextUserDistinguishedName, String contextPassword)
throws NamingException;
public Set<String> lookupUserDistinguishedNames(String searchBase, String searchFilter, String contextUserDistinguishedName, String contextPassword)
throws NamingException
{
return ldapClient.executeLdapQuery(
contextUserDistinguishedName,
contextPassword,
new LdapQuery.LdapQueryBuilder()
.withSearchBase(searchBase)
.withSearchFilter(searchFilter)
.build(),
searchResults -> {
ImmutableSet.Builder<String> distinguishedNames = ImmutableSet.builder();
while (searchResults.hasMore()) {
distinguishedNames.add(searchResults.next().getNameInNamespace());
}
return distinguishedNames.build();
});
}
}
Loading

0 comments on commit 9e0b2e5

Please sign in to comment.