diff --git a/auth/base/src/main/java/org/wildfly/security/auth/principal/NamePrincipal.java b/auth/base/src/main/java/org/wildfly/security/auth/principal/NamePrincipal.java index 04912cc17d0..5cd841353e1 100644 --- a/auth/base/src/main/java/org/wildfly/security/auth/principal/NamePrincipal.java +++ b/auth/base/src/main/java/org/wildfly/security/auth/principal/NamePrincipal.java @@ -93,4 +93,36 @@ public boolean equals(final NamePrincipal obj) { public String toString() { return name; } + + /** + * Attempt to convert the given principal to a {@code NamePrincipal}. + * + * @param principal the original principal + * @return the {@code NamePrincipal} or {@code null} if the principal cannot be converted + */ + public static NamePrincipal from(Principal principal) { + if (principal instanceof NamePrincipal) { + return (NamePrincipal) principal; + } + return isConvertibleTo(principal) ? new NamePrincipal(principal.getName()) : null; + } + + /** + * Check if the given principal could be converted to a {@code NamePrincipal}. + * + * @param principal the original principal + * @return {@code true} if the principal can be converted to a {@code NamePrincipal} and {@code false} otherwise + */ + public static boolean isConvertibleTo(Principal principal) { + if (principal instanceof NamePrincipal) { + return true; + } + if (principal != null) { + String name = principal.getName(); + if (name != null && ! name.isEmpty()) { + return true; + } + } + return false; + } } diff --git a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java index 6cd251147ea..65e19ca38eb 100644 --- a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java +++ b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java @@ -214,11 +214,11 @@ private String nameFor(Path path) { } public RealmIdentity getRealmIdentity(final Principal principal) { - return principal instanceof NamePrincipal ? getRealmIdentity(principal.getName(), false) : RealmIdentity.NON_EXISTENT; + return NamePrincipal.isConvertibleTo(principal) ? getRealmIdentity(principal.getName(), false) : RealmIdentity.NON_EXISTENT; } public ModifiableRealmIdentity getRealmIdentityForUpdate(final Principal principal) { - return principal instanceof NamePrincipal ? getRealmIdentity(principal.getName(), true) : ModifiableRealmIdentity.NON_EXISTENT; + return NamePrincipal.isConvertibleTo(principal) ? getRealmIdentity(principal.getName(), true) : ModifiableRealmIdentity.NON_EXISTENT; } @Override diff --git a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/JaasSecurityRealm.java b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/JaasSecurityRealm.java index d7cfc6dd10f..77b5b84c13b 100644 --- a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/JaasSecurityRealm.java +++ b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/JaasSecurityRealm.java @@ -86,7 +86,12 @@ public JaasSecurityRealm(final String loginConfiguration, final CallbackHandler @Override public RealmIdentity getRealmIdentity(final Principal principal) { - return principal instanceof NamePrincipal ? new JaasRealmIdentity(principal) : RealmIdentity.NON_EXISTENT; + if (principal instanceof NamePrincipal) { + return new JaasRealmIdentity(principal); + } else { + NamePrincipal namePrincipal = NamePrincipal.from(principal); + return namePrincipal != null ? new JaasRealmIdentity(namePrincipal) : RealmIdentity.NON_EXISTENT; + } } @Override diff --git a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java index 770cdf617fc..06dc93141fe 100644 --- a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java +++ b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java @@ -79,7 +79,7 @@ public KeyStoreBackedSecurityRealm(final KeyStore keyStore, final Supplier C getCredential(final Class credentialType, fin @Override public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { if (accountEntry.getPasswordRepresentation() == null || LegacyPropertiesSecurityRealm.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec) == SupportLevel.UNSUPPORTED) { - log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]", principal); + log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]", namePrincipal); return null; } @@ -150,7 +151,7 @@ public C getCredential(final Class credentialType, fin } else if (ALGORITHM_DIGEST_MD5.equals(algorithmName)) { clear = false; } else { - log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]: unsupported algorithm [%s]", principal, algorithmName); + log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]: unsupported algorithm [%s]", namePrincipal, algorithmName); return null; } @@ -191,7 +192,7 @@ public C getCredential(final Class credentialType, fin @Override public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { if (accountEntry.getPasswordRepresentation() == null || !(evidence instanceof PasswordGuessEvidence)) { - log.tracef("Unable to verify evidence for identity [%s]", principal); + log.tracef("Unable to verify evidence for identity [%s]", namePrincipal); return false; } final char[] guess = ((PasswordGuessEvidence) evidence).getGuess(); diff --git a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java index 50acc6f57a0..df5a9fc2846 100644 --- a/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java +++ b/auth/realm/base/src/main/java/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java @@ -147,7 +147,7 @@ public void setPasswordMap(final String name, final Password password) { @Override public RealmIdentity getRealmIdentity(final Principal principal) { - if (! (principal instanceof NamePrincipal)) { + if (! NamePrincipal.isConvertibleTo(principal)) { return RealmIdentity.NON_EXISTENT; } String name = rewriter.rewriteName(principal.getName()); diff --git a/auth/realm/jdbc/src/main/java/org/wildfly/security/auth/realm/jdbc/JdbcSecurityRealm.java b/auth/realm/jdbc/src/main/java/org/wildfly/security/auth/realm/jdbc/JdbcSecurityRealm.java index e5b82985334..4a622d59a25 100644 --- a/auth/realm/jdbc/src/main/java/org/wildfly/security/auth/realm/jdbc/JdbcSecurityRealm.java +++ b/auth/realm/jdbc/src/main/java/org/wildfly/security/auth/realm/jdbc/JdbcSecurityRealm.java @@ -67,7 +67,7 @@ public static JdbcSecurityRealmBuilder builder() { @Override public RealmIdentity getRealmIdentity(final Principal principal) { - if (! (principal instanceof NamePrincipal)) { + if (! NamePrincipal.isConvertibleTo(principal)) { return RealmIdentity.NON_EXISTENT; } return new JdbcRealmIdentity(principal.getName()); diff --git a/auth/realm/ldap/src/main/java/org/wildfly/security/auth/realm/ldap/LdapSecurityRealm.java b/auth/realm/ldap/src/main/java/org/wildfly/security/auth/realm/ldap/LdapSecurityRealm.java index 4852bdba707..1a320aa5640 100644 --- a/auth/realm/ldap/src/main/java/org/wildfly/security/auth/realm/ldap/LdapSecurityRealm.java +++ b/auth/realm/ldap/src/main/java/org/wildfly/security/auth/realm/ldap/LdapSecurityRealm.java @@ -176,7 +176,7 @@ private void registerIdentityChangeListener(final DirContext dirContext, final C } private ModifiableRealmIdentity getRealmIdentity(final Principal principal, final boolean exclusive) { - if (! (principal instanceof NamePrincipal)) { + if (! NamePrincipal.isConvertibleTo(principal)) { return ModifiableRealmIdentity.NON_EXISTENT; } String name = nameRewriter.rewriteName(principal.getName()); diff --git a/tests/base/src/test/java/org/wildfly/security/auth/server/IdentityPropagationTest.java b/tests/base/src/test/java/org/wildfly/security/auth/server/IdentityPropagationTest.java index 8ead433a57b..d4e793dd35f 100644 --- a/tests/base/src/test/java/org/wildfly/security/auth/server/IdentityPropagationTest.java +++ b/tests/base/src/test/java/org/wildfly/security/auth/server/IdentityPropagationTest.java @@ -22,6 +22,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -29,12 +31,15 @@ import org.junit.BeforeClass; import org.junit.Test; +import org.wildfly.security.auth.SupportLevel; import org.wildfly.security.auth.permission.LoginPermission; import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm; import org.wildfly.security.auth.realm.SimpleRealmEntry; import org.wildfly.security.authz.MapAttributes; import org.wildfly.security.authz.RoleDecoder; import org.wildfly.security.authz.Roles; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; import org.wildfly.security.permission.PermissionVerifier; /** @@ -48,6 +53,8 @@ public class IdentityPropagationTest { private static SecurityDomain domain1; private static SecurityDomain domain2; private static SecurityDomain domain3; + private static SecurityDomain domain4; + private static SecurityDomain domain5; @BeforeClass public static void setupSecurityDomains() { @@ -93,6 +100,23 @@ public static void setupSecurityDomains() { trustedSecurityDomains.add(domain2); builder.setTrustedSecurityDomainPredicate(trustedSecurityDomains::contains); domain3 = builder.build(); + + // domain4 contains a custom realm + builder = SecurityDomain.builder(); + builder.addRealm("customRealm", new CustomRealm()).build(); + builder.setDefaultRealmName("customRealm"); + builder.setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.from(new LoginPermission())); + domain4 = builder.build(); + + // domain5 trusts domain4 + builder = SecurityDomain.builder(); + builder.addRealm("usersRealm", realm1).setRoleMapper(rolesToMap -> Roles.of("UserRole")).build(); + builder.setDefaultRealmName("usersRealm"); + trustedSecurityDomains = new HashSet<>(); + trustedSecurityDomains.add(domain4); + builder.setTrustedSecurityDomainPredicate(trustedSecurityDomains::contains); + builder.setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.from(new LoginPermission())); + domain5 = builder.build(); } @Test @@ -153,6 +177,28 @@ public void testInflowFromSameDomain() throws Exception { assertTrue(inflowedIdentity.getAttributes().get("roles").containsAll(establishedIdentity.getAttributes().get("roles"))); } + @Test + public void testInflowSecurityIdentityWithCustomPrincipal() throws Exception { + // establish an identity using domain4 + ServerAuthenticationContext context = domain4.createNewAuthenticationContext(); + assertTrue(context.verifyEvidence(new Evidence() { + @Override + public Principal getPrincipal() { + return new CustomRealm.CustomPrincipal("joe"); + } + })); + assertTrue(context.authorize()); + SecurityIdentity establishedIdentity = context.getAuthorizedIdentity(); + + // import the established identity into domain5 + context = domain5.createNewAuthenticationContext(); + assertTrue(context.importIdentity(establishedIdentity)); + SecurityIdentity inflowedIdentity = context.getAuthorizedIdentity(); + assertEquals("joe", inflowedIdentity.getPrincipal().getName()); + assertEquals(domain5, inflowedIdentity.getSecurityDomain()); + assertTrue(inflowedIdentity.getRoles().contains("UserRole")); + } + private static void addUser(Map securityRealm, String userName, String roles) { MapAttributes attributes = new MapAttributes(); attributes.addAll(RoleDecoder.KEY_ROLES, Collections.singletonList(roles)); @@ -162,4 +208,98 @@ private static void addUser(Map securityRealm, String private SecurityIdentity getIdentityFromDomain(final SecurityDomain securityDomain, final String userName) { return securityDomain.getAnonymousSecurityIdentity().createRunAsIdentity(userName, false); } + + private static class CustomRealm implements SecurityRealm { + + @Override + public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException { + return new CustomRealmIdentity(new CustomPrincipal(principal.getName())); + } + + @Override + public RealmIdentity getRealmIdentity(Evidence evidence) throws RealmUnavailableException { + throw new RealmUnavailableException(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + private class CustomRealmIdentity implements RealmIdentity { + + CustomPrincipal principal; + + public CustomRealmIdentity(CustomPrincipal principal) { + this.principal = principal; + } + + @Override + public Principal getRealmIdentityPrincipal() { + return principal; + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + @Override + public C getCredential(Class credentialType) throws RealmUnavailableException { + return null; + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + @Override + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + return principal != null; + } + + @Override + public boolean exists() throws RealmUnavailableException { + return principal != null; + } + } + + private static class CustomPrincipal implements Principal { + private final String name; + + public CustomPrincipal(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CustomPrincipal that = (CustomPrincipal) o; + if (! name.equals(that.name)) return false; + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return name; + } + } + } }