Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

<cleanthat.version>2.17</cleanthat.version>
<error-prone.version>2.24.1</error-prone.version>
<google-java-format.version>1.19.2</google-java-format.version>

<maven-clean-plugin.version>3.3.2</maven-clean-plugin.version>
<maven-compiler-plugin.version>3.12.1</maven-compiler-plugin.version>
Expand Down
47 changes: 30 additions & 17 deletions src/main/java/tech/stackable/hadoop/StackableGroupMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,23 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.fasterxml.jackson.databind.type.TypeFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.GroupMappingServiceProvider;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StackableGroupMapper implements GroupMappingServiceProvider {

private static final Logger LOG = LoggerFactory.getLogger(StackableGroupMapper.class);

public static final String OPA_MAPPING_URL_PROP = "hadoop.security.group.mapping.opa.policy.url";
// response base field: see https://www.openpolicyagent.org/docs/latest/rest-api/#response-message
private static final String OPA_RESULT_FIELD = "result";

private static final Logger LOG = LoggerFactory.getLogger(StackableGroupMapper.class);
private final HttpClient httpClient = HttpClient.newHttpClient();
private final ObjectMapper json;
private URI opaUri;
Expand Down Expand Up @@ -59,21 +53,36 @@ public StackableGroupMapper() {
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

private static class OpaQueryResult {
public List<String> result;
}

/**
* Returns list of groups for a user.
* Returns list of groups for a user. Internally Hadoop will pass the short name to this function,
* but this prevents us from effectively separating users with the same names but with different
* kerberos principals. For this reason the user name is extracted from the UserGroupInformation
* instead (giving us the full name), defaulting to the original name if this is not possible.
*
* @param user get groups for this user
* @param user get groups from the associated user group information for this user
* @return list of groups for a given user
*/
@Override
public List<String> getGroups(String user) {
LOG.info("Calling StackableGroupMapper.getGroups for user \"{}\"", user);

OpaGroupsQuery query = new OpaGroupsQuery(new OpaGroupsQuery.OpaGroupsQueryInput(user));
String workingUser = user;
try {
UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
LOG.debug(
"Current user [{}] with user-name [{}] and short-name [{}]",
currentUser,
currentUser.getUserName(),
currentUser.getShortUserName());
workingUser = currentUser.getUserName();
} catch (IOException e) {
LOG.warn(
"Unable to extract name from UserGroupInformation, defaulting to \"{}\": {}",
user,
e.getMessage());
}

OpaGroupsQuery query = new OpaGroupsQuery(new OpaGroupsQuery.OpaGroupsQueryInput(workingUser));

String body;
try {
Expand Down Expand Up @@ -115,7 +124,7 @@ public List<String> getGroups(String user) {
}
List<String> groups = result.result;

LOG.debug("Groups for \"{}\": {}", user, groups);
LOG.debug("Groups for \"{}\": {}", workingUser, groups);

return groups;
}
Expand All @@ -139,4 +148,8 @@ public void cacheGroupsAdd(List<String> groups) {
"ignoring cacheGroupsAdd for groups [{}]: caching should be provided by the policy provider",
groups);
}

private static class OpaQueryResult {
public List<String> result;
}
}