Created
May 30, 2025 12:40
-
-
Save michael-o/39dff88679427badf2e617bf2285b437 to your computer and use it in GitHub Desktop.
PrincipalTransformingRealm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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