diff --git a/distribution/server-overlay/cli/keycloak-install-ha.cli b/distribution/server-overlay/cli/keycloak-install-ha.cli
index 4b8d399e36..6f61189630 100644
--- a/distribution/server-overlay/cli/keycloak-install-ha.cli
+++ b/distribution/server-overlay/cli/keycloak-install-ha.cli
@@ -4,6 +4,7 @@ embed-server --server-config=standalone-ha.xml
/subsystem=infinispan/cache-container=keycloak/transport=TRANSPORT:add(lock-timeout=60000)
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=realms:add(mode="SYNC")
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=users:add(mode="SYNC")
+/subsystem=infinispan/cache-container=keycloak/invalidation-cache=users/eviction=EVICTION:add(max-entries=10000,strategy=LRU)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:add(mode="SYNC",owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:add(mode="SYNC",owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
diff --git a/distribution/server-overlay/cli/keycloak-install.cli b/distribution/server-overlay/cli/keycloak-install.cli
index ce08e0a411..c29cd5f1e8 100644
--- a/distribution/server-overlay/cli/keycloak-install.cli
+++ b/distribution/server-overlay/cli/keycloak-install.cli
@@ -3,11 +3,12 @@ embed-server --server-config=standalone.xml
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
/subsystem=infinispan/cache-container=keycloak/local-cache=realms:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
+/subsystem=infinispan/cache-container=keycloak/local-cache=users/eviction=EVICTION:add(max-entries=10000,strategy=LRU)
/subsystem=infinispan/cache-container=keycloak/local-cache=sessions:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=offlineSessions:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=loginFailures:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=work:add()
-/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions:add()
-/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/transaction=TRANSACTION:add(mode=BATCH,locking=PESSIMISTIC)
+/subsystem=infinispan/cache-container=keycloak/local-cache=authorization:add()
+/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/eviction=EVICTION:add(max-entries=100,strategy=LRU)
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
/subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
index 3bad384976..24b772b39f 100755
--- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
@@ -193,9 +193,13 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
private Configuration getRevisionCacheConfig(boolean managed, long maxEntries) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.invocationBatching().enable().transaction().transactionMode(TransactionMode.TRANSACTIONAL);
- if (!managed) {
+
+ // Workaround: Use Dummy manager even in managed ( wildfly/eap ) environment. Without this workaround, there is an issue in EAP7 overlay.
+ // After start+end revisions batch is left the JTA transaction in committed state. This is incorrect and causes other issues afterwards.
+ // TODO: Investigate
+ // if (!managed)
cb.transaction().transactionManagerLookup(new DummyTransactionManagerLookup());
- }
+
cb.transaction().lockingMode(LockingMode.PESSIMISTIC);
cb.eviction().strategy(EvictionStrategy.LRU).type(EvictionType.COUNT).size(maxEntries);
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
index 9b01aceeef..1d118b285f 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.1.0.xml
@@ -159,7 +159,7 @@
-
+
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 85e316fb15..42fd549cb0 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -375,8 +375,15 @@ public class SamlProtocol implements LoginProtocol {
Document samlDocument = null;
try {
ResponseType samlModel = builder.buildModel();
- transformAttributeStatement(attributeStatementMappers, samlModel, session, userSession, clientSession);
- populateRoles(roleListMapper, samlModel, session, userSession, clientSession);
+ final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession);
+ populateRoles(roleListMapper, session, userSession, clientSession, attributeStatement);
+
+ // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
+ if (attributeStatement.getAttributes().size() > 0) {
+ AssertionType assertion = samlModel.getAssertions().get(0).getAssertion();
+ assertion.addStatement(attributeStatement);
+ }
+
samlModel = transformLoginResponse(loginResponseMappers, samlModel, session, userSession, clientSession);
samlDocument = builder.buildDocument(samlModel);
} catch (Exception e) {
@@ -437,19 +444,14 @@ public class SamlProtocol implements LoginProtocol {
}
}
- public void transformAttributeStatement(List> attributeStatementMappers, ResponseType response, KeycloakSession session, UserSessionModel userSession,
- ClientSessionModel clientSession) {
- AssertionType assertion = response.getAssertions().get(0).getAssertion();
+ public AttributeStatementType populateAttributeStatements(List> attributeStatementMappers, KeycloakSession session, UserSessionModel userSession,
+ ClientSessionModel clientSession) {
AttributeStatementType attributeStatement = new AttributeStatementType();
-
for (ProtocolMapperProcessor processor : attributeStatementMappers) {
processor.mapper.transformAttributeStatement(attributeStatement, processor.model, session, userSession, clientSession);
}
- // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
- if (attributeStatement.getAttributes().size() > 0) {
- assertion.addStatement(attributeStatement);
- }
+ return attributeStatement;
}
public ResponseType transformLoginResponse(List> mappers, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
@@ -459,17 +461,11 @@ public class SamlProtocol implements LoginProtocol {
return response;
}
- public void populateRoles(ProtocolMapperProcessor roleListMapper, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
+ public void populateRoles(ProtocolMapperProcessor roleListMapper, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
+ final AttributeStatementType existingAttributeStatement) {
if (roleListMapper == null)
return;
- AssertionType assertion = response.getAssertions().get(0).getAssertion();
- AttributeStatementType attributeStatement = new AttributeStatementType();
- roleListMapper.mapper.mapRoles(attributeStatement, roleListMapper.model, session, userSession, clientSession);
-
- // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
- if (attributeStatement.getAttributes().size() > 0) {
- assertion.addStatement(attributeStatement);
- }
+ roleListMapper.mapper.mapRoles(existingAttributeStatement, roleListMapper.model, session, userSession, clientSession);
}
public static String getLogoutServiceUrl(UriInfo uriInfo, ClientModel client, String bindingType) {
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index a8c4af75d3..7a2f985586 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -32,6 +32,7 @@
+ -
-
-
-
@@ -97,6 +98,7 @@
maven-surefire-plugin
+ ${exclude.test}
${exclude.console}
${exclude.account}
${exclude.client}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 5f8a51df68..5d12db4ca5 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -45,6 +45,7 @@ import javax.ws.rs.core.UriBuilder;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.representations.idm.EventRepresentation;
@@ -207,10 +208,9 @@ public class AccountTest extends TestRealmKeycloakTest {
testRealm.setPasswordPolicy(policy);
testRealm().update(testRealm);
}
- @Test
- public void changePasswordWithLengthPasswordPolicy() {
- setPasswordPolicy("length");
+ @Test
+ public void changePasswordWithBlankCurrentPassword() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
@@ -219,7 +219,130 @@ public class AccountTest extends TestRealmKeycloakTest {
Assert.assertEquals("Please specify password.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_MISSING).assertEvent();
- changePasswordPage.changePassword("password", "new-password", "new-password");
+ changePasswordPage.changePassword("password", "new", "new");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithLengthPasswordPolicy() {
+ setPasswordPolicy("length(8)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "1234", "1234");
+ Assert.assertEquals("Invalid password: minimum length 8.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+ changePasswordPage.changePassword("password", "12345678", "12345678");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithDigitsPolicy() {
+ setPasswordPolicy("digits(2)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "invalidPassword1", "invalidPassword1");
+ Assert.assertEquals("Invalid password: must contain at least 2 numerical digits.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+ changePasswordPage.changePassword("password", "validPassword12", "validPassword12");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithLowerCasePolicy() {
+ setPasswordPolicy("lowerCase(2)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "iNVALIDPASSWORD", "iNVALIDPASSWORD");
+ Assert.assertEquals("Invalid password: must contain at least 2 lower case characters.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+ changePasswordPage.changePassword("password", "vaLIDPASSWORD", "vaLIDPASSWORD");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithUpperCasePolicy() {
+ setPasswordPolicy("upperCase(2)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "Invalidpassword", "Invalidpassword");
+ Assert.assertEquals("Invalid password: must contain at least 2 upper case characters.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+
+ changePasswordPage.changePassword("password", "VAlidpassword", "VAlidpassword");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithSpecialCharsPolicy() {
+ setPasswordPolicy("specialChars(2)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "invalidPassword*", "invalidPassword*");
+ Assert.assertEquals("Invalid password: must contain at least 2 special characters.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+
+ changePasswordPage.changePassword("password", "validPassword*#", "validPassword*#");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithNotUsernamePolicy() {
+ setPasswordPolicy("notUsername(1)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "test-user@localhost", "test-user@localhost");
+ Assert.assertEquals("Invalid password: must not be equal to the username.", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+
+ changePasswordPage.changePassword("password", "newPassword", "newPassword");
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+ events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+ }
+
+ @Test
+ public void changePasswordWithRegexPatternsPolicy() {
+ setPasswordPolicy("regexPattern(^[A-Z]+#[a-z]{8}$)");
+
+ changePasswordPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+ changePasswordPage.changePassword("password", "invalidPassword", "invalidPassword");
+ Assert.assertEquals("Invalid password: fails to match regex pattern(s).", profilePage.getError());
+ events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
+
+
+ changePasswordPage.changePassword("password", "VALID#password", "VALID#password");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
index 1b1348d412..aef6e93c3d 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
@@ -77,10 +77,10 @@ public class PasswordPolicy extends Authentication {
public enum Type {
- HASH_ITERATIONS("HashIterations"), LENGTH("Length"), DIGITS("Digits"), LOWER_CASE("LowerCase"),
- UPPER_CASE("UpperCase"), SPECIAL_CHARS("SpecialChars"), NOT_USERNAME("NotUsername"),
- REGEX_PATTERN("RegexPattern"), PASSWORD_HISTORY("PasswordHistory"),
- FORCE_EXPIRED_PASSWORD_CHANGE("ForceExpiredPasswordChange");
+ HASH_ITERATIONS("Hashing Iterations"), LENGTH("Minimum Length"), DIGITS("Digits"), LOWER_CASE("Lowercase Characters"),
+ UPPER_CASE("Uppercase Characters"), SPECIAL_CHARS("Special Characters"), NOT_USERNAME("Not Username"),
+ REGEX_PATTERN("Regular Expression"), PASSWORD_HISTORY("Not Recently Used"),
+ FORCE_EXPIRED_PASSWORD_CHANGE("Expire Password"), HASH_ALGORITHM("Hashing Algorithm");
private String name;
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java
index 68da020b1a..e70acd4f30 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java
@@ -178,6 +178,12 @@ public class PasswordPolicyTest extends AbstractConsoleTest {
testUserCredentialsPage.resetPassword("firstPassword");
assertAlertDanger();
+
+ testUserCredentialsPage.resetPassword("thirdPassword");
+ assertAlertSuccess();
+
+ testUserCredentialsPage.resetPassword("firstPassword");
+ assertAlertSuccess();
}
}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
index 9537661a5e..1f47c1ea3f 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
@@ -73,7 +73,7 @@ public class SecurityDefensesTest extends AbstractRealmTest {
@Test
public void maxLoginFailuresTest() throws InterruptedException {
- final short secondsToWait = 3;
+ final short secondsToWait = 10; // For slower browsers/webdrivers (like IE) we need higher value
final short maxLoginFailures = 2;
bruteForceDetectionPage.form().setProtectionEnabled(true);
@@ -89,7 +89,7 @@ public class SecurityDefensesTest extends AbstractRealmTest {
@Test
public void quickLoginCheck() throws InterruptedException {
- final short secondsToWait = 3;
+ final short secondsToWait = 10;
bruteForceDetectionPage.form().setProtectionEnabled(true);
bruteForceDetectionPage.form().setMaxLoginFailures("100");
@@ -104,7 +104,7 @@ public class SecurityDefensesTest extends AbstractRealmTest {
@Test
public void maxWaitLoginFailures() throws InterruptedException {
- final short secondsToWait = 5;
+ final short secondsToWait = 15;
bruteForceDetectionPage.form().setProtectionEnabled(true);
bruteForceDetectionPage.form().setMaxLoginFailures("1");
@@ -120,7 +120,7 @@ public class SecurityDefensesTest extends AbstractRealmTest {
@Test
public void failureResetTime() throws InterruptedException {
final short failureResetTime = 3;
- final short waitIncrement = 3;
+ final short waitIncrement = 5;
bruteForceDetectionPage.form().setProtectionEnabled(true);
bruteForceDetectionPage.form().setMaxLoginFailures("1");
@@ -199,8 +199,8 @@ public class SecurityDefensesTest extends AbstractRealmTest {
wait *= 1000;
- log.debug("Wait: " + wait);
- Thread.sleep(wait);
+ log.info("Wait: " + wait);
+ pause(wait);
if (finalLogin) {
testRealmLoginPage.form().login(testUser);