Skip to content

Commit

Permalink
[ELY-2254] Provide a LoginModule compatible security realm.
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyllarr authored and ivassile committed Oct 5, 2023
1 parent 3dc896a commit fee243a
Show file tree
Hide file tree
Showing 10 changed files with 488 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.Param;
import org.jboss.logging.annotations.ValidIdRange;
import org.jboss.logging.annotations.ValidIdRanges;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityRealm;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;

/**
* Log messages and exceptions for Elytron.
*
Expand All @@ -55,7 +59,7 @@ interface ElytronMessages extends BasicLogger {

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 1007, value = "JAAS authentication failed for principal %s")
void debugJAASAuthenticationFailure(Principal principal, @Cause Throwable cause);
void debugInfoJaasAuthenticationFailure(Principal principal, @Cause Throwable cause);

@Message(id = 1008, value = "Failed to create login context")
RealmUnavailableException failedToCreateLoginContext(@Cause Throwable cause);
Expand Down Expand Up @@ -133,4 +137,13 @@ interface ElytronMessages extends BasicLogger {
@Message(id = 13001, value = "Realm is failing over.")
void realmFailover(@Cause RealmUnavailableException rue);

@Message(id = 13002, value = "%s does not handle a callback of type %s")
UnsupportedCallbackException unableToHandleCallback(@Param Callback callback, String callbackHandler, String callbackType);

@Message(id = 13003, value = "Failed to load JAAS configuration file.")
RealmUnavailableException failedToLoadJaasConfigFile();

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 13004, value = "JAAS logout failed for principal %s")
void debugInfoJaasLogoutFailure(Principal principal, @Cause Throwable cause);
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,7 @@ AuthorizedAuthenticationState doAuthorization(final boolean requireLoginPermissi

SecurityIdentity authorizedIdentity = Assert.assertNotNull(domain.transform(new SecurityIdentity(domain, authenticationPrincipal, realmInfo, authorizationIdentity, domain.getCategoryRoleMappers(), IdentityCredentials.NONE, IdentityCredentials.NONE)));
authorizedIdentity = authorizedIdentity.withPublicCredentials(publicCredentials).withPrivateCredentials(privateCredentials);

if (log.isTraceEnabled()) {
log.tracef("Authorizing principal %s.", authenticationPrincipal.getName());
if (authorizationIdentity != null) {
Expand All @@ -2062,8 +2063,8 @@ AuthorizedAuthenticationState doAuthorization(final boolean requireLoginPermissi
ElytronMessages.log.trace("Authorization succeed");
return new AuthorizedAuthenticationState(authorizedIdentity, authenticationPrincipal, realmInfo, realmIdentity, mechanismRealmConfiguration, mechanismConfiguration);
}
@Override

@Override
boolean authorize(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException {
final AuthorizedAuthenticationState authzState = doAuthorization(true);
if (authzState == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,25 @@

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.ServerAuthenticationContext;
import org.wildfly.security.auth.realm.JaasSecurityRealm;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.SecurityRealm;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.PublicKeyCredential;
import org.wildfly.security.evidence.PasswordGuessEvidence;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;

/**
* Testsuite for the {@link org.wildfly.security.auth.realm.JaasSecurityRealm}.
Expand All @@ -45,65 +49,165 @@
public class JaasSecurityRealmTest {

@BeforeClass
public static void init() {
System.setProperty("java.security.auth.login.config", JaasSecurityRealmTest.class.getResource("login.config").toString());
public static void beforeClass() {
System.setProperty("java.security.auth.login.config", JaasSecurityRealmTest.class.getResource("jaas-login.config").toString());
}

@AfterClass
public static void afterClass() {
System.clearProperty("java.security.auth.login.config");
}

@Test
public void testJaasSecurityRealm() throws Exception {
public void testSmokeJaasSecurityRealm() throws Exception {

// create a JAAS security realm with the default callback handler.
SecurityRealm realm = new JaasSecurityRealm("test");
// create a JAAS security realm with the file from system property and default callback handler.
SecurityRealm realm = new JaasSecurityRealm("Entry1");

// test the creation of a realm identity.
RealmIdentity realmIdentity = realm.getRealmIdentity(new NamePrincipal("elytron"));
assertNotNull("Unexpected null realm identity", realmIdentity);

// check the supported credential types (the default handler can only handle char[], String and ClearPassword credentials)..
// we do not allow to obtain the credentials from the JAAS realm
assertEquals("Invalid credential support", SupportLevel.UNSUPPORTED, realmIdentity.getCredentialAcquireSupport(PasswordCredential.class, "blah", null));
assertEquals("Invalid credential support", SupportLevel.UNSUPPORTED,
realmIdentity.getCredentialAcquireSupport(PublicKeyCredential.class, null, null));

// we do not know what type of evidence the custom realms support so the result should be possibly supported
assertEquals("Invalid credential support", SupportLevel.POSSIBLY_SUPPORTED, realmIdentity.getEvidenceVerifySupport(PasswordGuessEvidence.class, "blah"));

// the JAAS realm identity cannot be used to obtain credentials, so getCredential should always return null.
assertNull("Invalid non null credential", realmIdentity.getCredential(PasswordCredential.class, null));

// use the realm identity to verify all supported credentials - this will trigger a JAAS login that will use the test module.
// use the realm identity to verify provided credentials - this will trigger a JAAS login that will use the test module.
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("passwd12#$".toCharArray())));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("wrongpass".toCharArray())));

// get the authenticated realm identity after successfully verifying the credential.
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("passwd12#$".toCharArray())));
AuthorizationIdentity authRealmIdentity = realmIdentity.getAuthorizationIdentity();
assertNotNull("Unexpected null authenticated realm identity", authRealmIdentity);
// check if the authenticated identity returns the caller principal as set by the test login module.
// Principal authPrincipal = authRealmIdentity.getPrincipal();
// assertNotNull("Unexpected null principal", authPrincipal);
// assertEquals("Invalid principal name", new NamePrincipal("auth-caller"), authPrincipal);

// dispose the auth realm identity - should trigger a JAAS logout that clears the subject.
// TODO - some other solution is needed here! We can no longer force JAAS logout in an authorization scenario.
//authPrincipal = authRealmIdentity.getPrincipal();
// after the logout, the subject no longer contains a caller principal so the identity should return the same principal as the realm identity.
//assertNotNull("Unexpected null principal", authPrincipal);
//assertEquals("Invalid principal name", new NamePrincipal("elytron"), authPrincipal);
assertNotNull("Unexpected null authenticated realm identity", realmIdentity.getAuthorizationIdentity());
}

@Test(expected = IllegalArgumentException.class)
public void testEntryCannotBeNull() {
new JaasSecurityRealm(null);
}

@Test(expected = RealmUnavailableException.class)
public void testPathMustExist() throws RealmUnavailableException {
JaasSecurityRealm realm = new JaasSecurityRealm("entry", "this/path/does/not/exist");
RealmIdentity realmIdentity = realm.getRealmIdentity(new NamePrincipal("javajoe"));
realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray()));
}

@Test
public void testJaasSecurityRealmWithCustomCallbackHandler() throws Exception {

// create a JAAS realm that takes a custom callback handler.
SecurityRealm realm = new JaasSecurityRealm("test", new TestCallbackHandler());
SecurityRealm realm = new JaasSecurityRealm("Entry1", null, null, new TestCallbackHandler());
// create a new realm identity using the realm.
RealmIdentity realmIdentity = realm.getRealmIdentity(new NamePrincipal("javajoe"));
// verify the credentials using the custom callback handler.
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("wrongpass".toCharArray())));
}

@Test
public void testJaasSecurityRealmWithEntry2() throws Exception {

// create a JAAS realm that takes a custom callback handler.
SecurityRealm realm = new JaasSecurityRealm("Entry2", null, null, new TestCallbackHandler());
// create a new realm identity using the realm.
RealmIdentity realmIdentity = realm.getRealmIdentity(new NamePrincipal("javajoe"));
// verify the credentials using the custom callback handler.
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("wrongpass".toCharArray())));

assertEquals("Invalid credential support", SupportLevel.SUPPORTED, realmIdentity.getEvidenceVerifySupport(PasswordGuessEvidence.class, null));
assertEquals("Invalid credential support", SupportLevel.UNSUPPORTED, realmIdentity.getEvidenceVerifySupport(X509PeerCertificateChainEvidence.class, null));

realmIdentity = realm.getRealmIdentity(new NamePrincipal("userFromTestModule2"));
// verify the credentials using the custom callback handler.
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("userPassword".toCharArray())));
}

@Test
public void testJaasSecurityRealmWithConfiguredPathToJAASConfigFile() throws Exception {

SecurityRealm realm = new JaasSecurityRealm("Entry1", "./src/test/resources/org/wildfly/security/auth/jaas-login2.config", null);
RealmIdentity realmIdentity = realm.getRealmIdentity(new NamePrincipal("javajoe"));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));

realmIdentity = realm.getRealmIdentity(new NamePrincipal("userFromTestModule2"));
assertFalse(realmIdentity.verifyEvidence(new PasswordGuessEvidence("wrongpass".toCharArray())));
assertTrue(realmIdentity.verifyEvidence(new PasswordGuessEvidence("userPassword".toCharArray())));

SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", realm).build()
.setPermissionMapper(((permissionMappable, roles) -> LoginPermission.getInstance()))
.build();
ServerAuthenticationContext sac1 = securityDomain.createNewAuthenticationContext();
sac1.setAuthenticationName("userFromTestModule2");
assertFalse(sac1.verifyEvidence(new PasswordGuessEvidence("incorrectPassword".toCharArray())));
assertTrue(sac1.verifyEvidence(new PasswordGuessEvidence("userPassword".toCharArray())));
Assert.assertTrue(sac1.authorize());
Assert.assertTrue(sac1.exists());
}

@Test
public void testJaasAuthorizationIdentityRoles() throws Exception { // is in role
SecurityRealm realm = new JaasSecurityRealm("Entry1", null, null, new TestCallbackHandler());
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", realm).build()
.setPermissionMapper(((permissionMappable, roles) -> LoginPermission.getInstance()))
.build();
ServerAuthenticationContext sac1 = securityDomain.createNewAuthenticationContext();
sac1.setAuthenticationPrincipal(new NamePrincipal("javajoe"));
assertTrue(sac1.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
Assert.assertTrue(sac1.authorize());
Assert.assertTrue(sac1.exists());
Assert.assertTrue(sac1.getAuthorizedIdentity().getRoles().contains("Admin"));
Assert.assertTrue(sac1.getAuthorizedIdentity().getRoles().contains("User"));
Assert.assertTrue(sac1.getAuthorizedIdentity().getRoles().contains("Guest"));
Assert.assertFalse(sac1.getAuthorizedIdentity().getRoles().contains("Non_existent_role"));
}

@Test
public void testJaasRealmAttributes() throws Exception {
SecurityRealm realm = new JaasSecurityRealm("Entry1", null, null, new TestCallbackHandler());
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", realm).build()
.setPermissionMapper(((permissionMappable, roles) -> LoginPermission.getInstance()))
.build();
ServerAuthenticationContext sac1 = securityDomain.createNewAuthenticationContext();
sac1.setAuthenticationPrincipal(new NamePrincipal("javajoe"));
assertTrue(sac1.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
Assert.assertTrue(sac1.authorize());
Assert.assertTrue(sac1.exists());
Assert.assertTrue(sac1.getAuthorizedIdentity().getAttributes().containsKey("NamePrincipal"));
Assert.assertEquals("whoami", sac1.getAuthorizedIdentity().getAttributes().get("NamePrincipal").get(0));
Assert.assertEquals("anonymous", sac1.getAuthorizedIdentity().getAttributes().get("AnonymousPrincipal").get(0));
Assert.assertNotEquals("non_existent_attribute", sac1.getAuthorizedIdentity().getAttributes().get("NamePrincipal").get(0));
Assert.assertNotEquals("whoami", sac1.getAuthorizedIdentity().getAttributes().get("NonExistentAttributeKey").get(0));
Assert.assertEquals("Admin", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(0));
Assert.assertEquals("User", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(1));
Assert.assertEquals("Guest", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(2));
}

@Test
public void testJaasRealmWithProvidedClassLoader() throws Exception {
SecurityRealm realm = new JaasSecurityRealm("Entry1", null, TestLoginModule2.class.getClassLoader());
SecurityDomain domainWithRewriter = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", realm).build()
.setPermissionMapper(((permissionMappable, roles) -> LoginPermission.getInstance()))
.build();
ServerAuthenticationContext sac1 = domainWithRewriter.createNewAuthenticationContext();
sac1.setAuthenticationPrincipal(new NamePrincipal("javajoe"));
assertTrue(sac1.verifyEvidence(new PasswordGuessEvidence("$#21pass".toCharArray())));
Assert.assertTrue(sac1.authorize());
Assert.assertTrue(sac1.exists());
Assert.assertTrue(sac1.getAuthorizedIdentity().getAttributes().containsKey("NamePrincipal"));
Assert.assertEquals("whoami", sac1.getAuthorizedIdentity().getAttributes().get("NamePrincipal").get(0));
Assert.assertEquals("anonymous", sac1.getAuthorizedIdentity().getAttributes().get("AnonymousPrincipal").get(0));
Assert.assertNotEquals("non_existent_attribute", sac1.getAuthorizedIdentity().getAttributes().get("NamePrincipal").get(0));
Assert.assertNotEquals("whoami", sac1.getAuthorizedIdentity().getAttributes().get("NonExistentAttributeKey").get(0));
Assert.assertEquals("Admin", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(0));
Assert.assertEquals("User", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(1));
Assert.assertEquals("Guest", sac1.getAuthorizedIdentity().getAttributes().get("Roles").get(2));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.security.Principal;

import org.wildfly.security.evidence.Evidence;
Expand Down Expand Up @@ -57,7 +56,7 @@ public void setSecurityInfo(final Principal principal, final Object evidence) {
}

@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
if (callbacks == null)
throw new IllegalArgumentException("The callbacks argument cannot be null");

Expand Down
Loading

0 comments on commit fee243a

Please sign in to comment.