Skip to content

Commit

Permalink
Merge branch '1.15.x' into 2.2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Skyllarr committed Feb 9, 2024
2 parents d8807e7 + cc45576 commit 785ce87
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,21 @@ public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableExceptio
if (evidence instanceof PasswordGuessEvidence) {
if (credentials.canVerify(evidence)) {
log.tracef("verifyEvidence For principal='%s' using cached credential", principal);
return credentials.verify(providerSupplier, evidence);
boolean credentialsVerified = credentials.verify(providerSupplier, evidence);
if (!credentialsVerified) {
// since verification failed then verify evidence directly on an identity
log.tracef("verifyEvidence for principal='%1$s' using cached credential failed, so trying verifyEvidence for principal='%1$s' using underlying security realm", principal);
char[] guess = ((PasswordGuessEvidence) evidence).getGuess();
Password password = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, guess);
if (identity.verifyEvidence(evidence)) {
credentials = credentials.without(PasswordCredential.class);
credentials = credentials.withCredential(new PasswordCredential(password));
attributes = null;
authorizationIdentity = null;
return true;
}
}
return credentialsVerified;
}
Credential credential = identity.getCredential(PasswordCredential.class);
if (credential != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,27 @@
package org.wildfly.security.auth.realm.cache;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.wildfly.security.auth.server.ServerUtils.ELYTRON_PASSWORD_PROVIDERS;

import java.security.Principal;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.password.WildFlyElytronPasswordProvider;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.auth.realm.CacheableSecurityRealm;
Expand Down Expand Up @@ -65,6 +72,16 @@ public class SecurityRealmIdentityCacheTest {

private AtomicInteger realmHitCount = new AtomicInteger();

@BeforeClass
public static void onBefore() {
Security.addProvider(WildFlyElytronPasswordProvider.getInstance());
}

@AfterClass
public static void onAfter() {
Security.removeProvider(WildFlyElytronPasswordProvider.getInstance().getName());
}

@Test
public void testRealmIdentitySimpleJavaMapCache() throws Exception {
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", createSecurityRealm(createRealmIdentityLRUCache())).build()
Expand Down Expand Up @@ -115,6 +132,132 @@ public void testMaxAge() throws Exception {
assertEquals(2, realmHitCount.get());
}

@Test
public void testPasswordUpdatedExternallyShouldPass() throws Exception {
SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
addUser(users, "joe", "User", "originalPassword");
realm.setIdentityMap(users);

CachingSecurityRealm cachingSecurityRealm = getSimpleLRUCachingSecurityRealm(realm);
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", cachingSecurityRealm).build()
.setPermissionMapper((permissionMappable, roles) -> LoginPermission.getInstance())
.build();
ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext();
sac.setAuthenticationName("joe");
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray())));
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray())));
users = new HashMap<>();
addUser(users, "joe", "User", "updatedPassword");
realm.setIdentityMap(users);
ServerAuthenticationContext secondAuthentication = securityDomain.createNewAuthenticationContext();
secondAuthentication.setAuthenticationName("joe");
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray()))); // will pass because old credential is cached
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("updatedPassword".toCharArray()))); // will pass because caching realm will invoke underlying realm when auth fails with cached credential
}

@Test
public void testAuthorizationIdentityAndAttributesWereUpdated() throws Exception {
SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
addUser(users, "joe", "User", "originalPassword");
realm.setIdentityMap(users);

CachingSecurityRealm cachingSecurityRealm = getSimpleLRUCachingSecurityRealm(realm);
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", cachingSecurityRealm).build()
.setPermissionMapper((permissionMappable, roles) -> LoginPermission.getInstance())
.build();
ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext();
sac.setAuthenticationName("joe");
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray())));
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray())));
users = new HashMap<>();
addUser(users, "joe", Arrays.asList("UpdatedUserRole", "UpdatedUserRole2"), "updatedPassword");
realm.setIdentityMap(users);
ServerAuthenticationContext secondAuthentication = securityDomain.createNewAuthenticationContext();
secondAuthentication.setAuthenticationName("joe");
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("updatedPassword".toCharArray()))); // will pass because caching realm will invoke underlying realm when auth fails with cached credential

//check that attributes in authorization identity were updated
Attributes joeUpdatedAttributes = realm.getRealmIdentity(new NamePrincipal("joe")).getAuthorizationIdentity().getAttributes();
assertEquals("Only Roles attribute should be configured", joeUpdatedAttributes.size(), 1); // no attributes other than Roles were configured
assertEquals("Attribute were not updated properly", joeUpdatedAttributes.get("Roles").size(), 2);
assertEquals("Attribute were not updated properly", joeUpdatedAttributes.get("Roles").get(0), "UpdatedUserRole");
assertEquals("Attribute were not updated properly", joeUpdatedAttributes.get("Roles").get(1), "UpdatedUserRole2");
}

@Test
public void testPasswordUpdatedExternallyShouldPass2() throws Exception {
SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
addUser(users, "joe", "User", "originalPassword");
realm.setIdentityMap(users);

CachingSecurityRealm cachingSecurityRealm = getSimpleLRUCachingSecurityRealm(realm);
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", cachingSecurityRealm).build()
.setPermissionMapper((permissionMappable, roles) -> LoginPermission.getInstance())
.build();
ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext();
sac.setAuthenticationName("joe");
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray()))); // will cache credential "originalPassword"
users = new HashMap<>();
addUser(users, "joe", "User", "updatedPassword");
realm.setIdentityMap(users);
ServerAuthenticationContext secondAuthentication = securityDomain.createNewAuthenticationContext();
secondAuthentication.setAuthenticationName("joe");
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray()))); // will pass because originalPassword credential is still cached
assertFalse(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("wrongPassword".toCharArray()))); // wrong password will fail
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("updatedPassword".toCharArray()))); // updated password should pass because caching realm will invoke underlying realm if authentication with cached credential failed
}

@Test
public void testUpdatedPasswordWillBeCachedAndOutdatedWillFail() throws Exception {
SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
addUser(users, "joe", "User", "originalPassword");
realm.setIdentityMap(users);

CachingSecurityRealm cachingSecurityRealm = getSimpleLRUCachingSecurityRealm(realm);
SecurityDomain securityDomain = SecurityDomain.builder().setDefaultRealmName("default").addRealm("default", cachingSecurityRealm).build()
.setPermissionMapper((permissionMappable, roles) -> LoginPermission.getInstance())
.build();
ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext();
sac.setAuthenticationName("joe");
assertTrue(sac.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray()))); // will cache credential "originalPassword"
users = new HashMap<>();
addUser(users, "joe", "User", "updatedPassword");
realm.setIdentityMap(users);
ServerAuthenticationContext secondAuthentication = securityDomain.createNewAuthenticationContext();
secondAuthentication.setAuthenticationName("joe");
assertTrue(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("updatedPassword".toCharArray()))); // externally updated will pass
assertFalse(secondAuthentication.verifyEvidence(new PasswordGuessEvidence("originalPassword".toCharArray()))); // outdated password will fail because new credential was cached in previous auth
}

private CachingSecurityRealm getSimpleLRUCachingSecurityRealm(SimpleMapBackedSecurityRealm realm) {
return new CachingSecurityRealm(new CacheableSecurityRealm() {
@Override
public void registerIdentityChangeListener(Consumer<Principal> listener) {

}

@Override
public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException {
realmHitCount.incrementAndGet();
return realm.getRealmIdentity(principal);
}

@Override
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realm.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}

@Override
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
return getEvidenceVerifySupport(evidenceType, algorithmName);
}
}, createRealmIdentityLRUCache());
}

private SecurityRealm createSecurityRealm(RealmIdentityCache cache) {
SimpleMapBackedSecurityRealm realm = new SimpleMapBackedSecurityRealm();
Map<String, SimpleRealmEntry> users = new HashMap<>();
Expand Down Expand Up @@ -169,22 +312,33 @@ public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> cred
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
return getEvidenceVerifySupport(evidenceType, algorithmName);
}
}, cache, ELYTRON_PASSWORD_PROVIDERS) {
}, cache) {
};
}

private void addUser(Map<String, SimpleRealmEntry> securityRealm, String userName, String roles) {
addUser(securityRealm, userName, roles, null);
}

private void addUser(Map<String, SimpleRealmEntry> securityRealm, String userName, String roles, String password) {
addUser(securityRealm, userName, Collections.singletonList(roles), password);
}

private void addUser(Map<String, SimpleRealmEntry> securityRealm, String userName, List<String> roles, String password) {
if (password == null) {
password = "password";
}
List<Credential> credentials;
try {
credentials = Collections.singletonList(
new PasswordCredential(
PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, ELYTRON_PASSWORD_PROVIDERS).generatePassword(
new ClearPasswordSpec("password".toCharArray()))));
PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR).generatePassword(
new ClearPasswordSpec(password.toCharArray()))));
} catch (Exception e) {
throw new RuntimeException(e);
}
MapAttributes attributes = new MapAttributes();
attributes.addAll(RoleDecoder.KEY_ROLES, Collections.singletonList(roles));
attributes.addAll(RoleDecoder.KEY_ROLES, roles);
securityRealm.put(userName, new SimpleRealmEntry(credentials, attributes));
}

Expand Down

0 comments on commit 785ce87

Please sign in to comment.