Skip to content

Commit

Permalink
Add LDAP group provider plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mosiac1 committed Sep 20, 2024
1 parent c5cb5aa commit d6f6539
Show file tree
Hide file tree
Showing 21 changed files with 2,025 additions and 0 deletions.
6 changes: 6 additions & 0 deletions core/trino-server/src/main/provisio/trino.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@
</artifact>
</artifactSet>

<artifactSet to="plugin/ldap-group-provider">
<artifact id="${project.groupId}:trino-ldap-group-provider:zip:${project.version}">
<unpack />
</artifact>
</artifactSet>

<artifactSet to="plugin/local-file">
<artifact id="${project.groupId}:trino-local-file:zip:${project.version}">
<unpack />
Expand Down
150 changes: 150 additions & 0 deletions plugin/trino-ldap-group-provider/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.trino</groupId>
<artifactId>trino-root</artifactId>
<version>444-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>trino-ldap-group-provider</artifactId>
<packaging>trino-plugin</packaging>
<description>Trino - LDAP Group Provider</description>

<properties>
<air.main.basedir>${project.parent.basedir}</air.main.basedir>
</properties>

<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>bootstrap</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>configuration</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>log</artifactId>
</dependency>

<dependency>
<groupId>io.trino</groupId>
<artifactId>trino-plugin-toolkit</artifactId>
</dependency>

<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>slice</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-context</artifactId>
<scope>provided</scope>
</dependency>

<!-- Trino SPI -->
<dependency>
<groupId>io.trino</groupId>
<artifactId>trino-spi</artifactId>
<scope>provided</scope>
</dependency>

<!-- for testing - Trino -->
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.trino</groupId>
<artifactId>trino-main</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.trino</groupId>
<artifactId>trino-testing</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.trino</groupId>
<artifactId>trino-testing-containers</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${dep.junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${dep.junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${dep.junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
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.ldapgroup;

import jakarta.inject.Qualifier;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Qualifier
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface ForLdapUserSearchDelegate
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.ldapgroup;

import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import io.airlift.log.Logger;
import io.trino.spi.classloader.ThreadContextClassLoader;
import io.trino.spi.security.GroupProvider;

import javax.naming.NamingException;

import java.util.List;
import java.util.Optional;
import java.util.Set;

import static java.util.Objects.requireNonNull;

public class LdapGroupProvider
implements GroupProvider
{
private static final Logger log = Logger.get(LdapGroupProvider.class);

private final String userMemberOfAttribute;
private final LdapGroupProviderClient ldapGroupClient;

@Inject
public LdapGroupProvider(LdapGroupProviderClient ldapGroupClient, LdapGroupProviderConfig config)
{
this.ldapGroupClient = requireNonNull(ldapGroupClient, "ldap client is null");
this.userMemberOfAttribute = config.getLdapUserMemberOfAttribute();
}

@Override
public Set<String> getGroups(String user)
{
try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(getClass().getClassLoader())) {
ImmutableSet.Builder<String> groupsBuilder = ImmutableSet.builder();
Optional<LdapSearchUserResult> userSearchResultOpt;
try {
userSearchResultOpt = ldapGroupClient.getUser(user);
}
catch (NamingException e) {
throw new RuntimeException(String.format("Error loading user %s from LDAP", user), e);
}

if (userSearchResultOpt.isEmpty()) {
log.warn("User %s does not exist in LDAP server", user);
return ImmutableSet.of();
}

LdapSearchUserResult userSearchResult = userSearchResultOpt.get();
log.debug("Fetched user DN and search attributes from LDAP for user %s - %s", user, userSearchResult);

groupsBuilder.addAll(userSearchResult
.userAttributes()
.entrySet()
.stream()
.filter(e -> !e.getKey().equalsIgnoreCase(userMemberOfAttribute))
.map(e -> e
.getValue()
.stream()
.map(v -> String.format("%s=%s", e.getKey(), v))
.toList())
.flatMap(List::stream)
.iterator());

try {
List<String> groups = ldapGroupClient.getGroups(userSearchResult);
log.debug("Fetched LDAP groups for user %s - %s", user, groups);
groupsBuilder.addAll(groups);
}
catch (NamingException e) {
throw new RuntimeException(String.format("Error loading groups for user %s from LDAP", user), e);
}

Set<String> groups = groupsBuilder.build();
log.debug("Loaded LDAP groups for user %s - %s", user, groups);
return groups;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.ldapgroup;

import javax.naming.NamingException;

import java.util.List;
import java.util.Optional;

public interface LdapGroupProviderClient
{
Optional<LdapSearchUserResult> getUser(String user)
throws NamingException;

List<String> getGroups(LdapSearchUserResult searchUserResult)
throws NamingException;
}
Loading

0 comments on commit d6f6539

Please sign in to comment.