Skip to content

Instantly share code, notes, and snippets.

@michael-o
Created May 30, 2025 12:40
Show Gist options
  • Save michael-o/39dff88679427badf2e617bf2285b437 to your computer and use it in GitHub Desktop.
Save michael-o/39dff88679427badf2e617bf2285b437 to your computer and use it in GitHub Desktop.
PrincipalTransformingRealm
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Map.Entry;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.realm.CombinedRealm;
import org.apache.tomcat.util.file.ConfigurationSource.Resource;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSName;
import net.sf.michaelo.tomcat.realm.ActiveDirectoryPrincipal;
import net.sf.michaelo.tomcat.realm.Sid;
public class PrincipalTransformingRealm extends CombinedRealm {
private String roleHistoryFile = "webapp:/WEB-INF/role-history.properties";
private String attributeMappingFile = "webapp:/WEB-INF/attribute-mapping.properties";
private final Map<String, String[]> roleHistories = new HashMap<>();
private final Map<String, String> attributeMappings = new HashMap<>();
public void setRoleHistoryFile(String roleHistoryFile) {
Objects.requireNonNull(roleHistoryFile, "roleHistoryFile cannot be null");
if (roleHistoryFile.isEmpty()) {
throw new IllegalArgumentException("roleHistoryFile cannot be empty");
}
this.roleHistoryFile = roleHistoryFile;
}
public void setAttributeMappingFile(String attributeMappingFile) {
Objects.requireNonNull(attributeMappingFile, "attributeMappingFile cannot be null");
if (attributeMappingFile.isEmpty()) {
throw new IllegalArgumentException("attributeMappingFile cannot be empty");
}
this.attributeMappingFile = attributeMappingFile;
}
@Override
protected void startInternal() throws LifecycleException {
Container container = getContainer();
if (!(container instanceof Context)) {
return;
}
Context context = (Context) container;
Properties props = new Properties();
try (Resource resource = context.findConfigFileResource(roleHistoryFile)) {
props.load(resource.getInputStream());
} catch (IOException e) {
throw new IllegalStateException(
"Failed to load role history file '" + roleHistoryFile + "'", e);
}
for (Entry<Object,Object> prop : props.entrySet()) {
String role = (String) prop.getKey();
String[] roleHistory = ((String) prop.getValue()).split(",");
roleHistories.put(role, roleHistory);
}
props.clear();
try (Resource resource = context.findConfigFileResource(attributeMappingFile)) {
props.load(resource.getInputStream());
} catch (IOException e) {
throw new IllegalStateException(
"Failed to load attribute mapping file '" + attributeMappingFile + "'", e);
}
for (Entry<Object,Object> prop : props.entrySet()) {
String attribute = (String) prop.getKey();
String attributeMapping = (String) prop.getValue();
attributeMappings.put(attribute, attributeMapping);
}
super.startInternal();
}
@Override
public Principal authenticate(GSSContext gssContext, boolean storeCred) {
Principal principal = super.authenticate(gssContext, storeCred);
return transformPrincipal(principal);
}
@Override
public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
Principal principal = super.authenticate(gssName, gssCredential);
return transformPrincipal(principal);
}
@Override
public Principal authenticate(X509Certificate[] userCerts) {
Principal principal = super.authenticate(userCerts);
return transformPrincipal(principal);
}
private Principal transformPrincipal(Principal principal) {
if (principal == null)
return null;
if (!(principal instanceof ActiveDirectoryPrincipal))
return principal;
ActiveDirectoryPrincipal adp = (ActiveDirectoryPrincipal) principal;
GSSName gssName = adp.getGssName();
Sid sid = adp.getSid();
List<String> roles = new ArrayList<>(Arrays.asList(adp.getRoles()));
GSSCredential gssCredential = adp.getGssCredential();
Map<String, Object> additionalAttributes = new HashMap<>(adp.getAdditionalAttributes());
for (Map.Entry<String, String[]> roleHistory : roleHistories.entrySet()) {
if (roles.contains(roleHistory.getKey()))
roles.addAll(Arrays.asList(roleHistory.getValue()));
}
for (Map.Entry<String, String> attributeMapping : attributeMappings.entrySet()) {
if (additionalAttributes.containsKey(attributeMapping.getKey())) {
Object val = additionalAttributes.remove(attributeMapping.getKey());
additionalAttributes.put(attributeMapping.getValue(), val);
}
}
return new ActiveDirectoryPrincipal(gssName, sid, roles, gssCredential, additionalAttributes);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment