Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
534ee2e50c
10 changed files with 171 additions and 38 deletions
|
@ -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")
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
|
|
|
@ -159,7 +159,7 @@
|
|||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="VARCHAR(4096)"/>
|
||||
<column name="VALUE" type="VARCHAR(4000)"/>
|
||||
</createTable>
|
||||
<createTable tableName="COMPONENT">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
|
|
|
@ -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<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers, ResponseType response, KeycloakSession session, UserSessionModel userSession,
|
||||
ClientSessionModel clientSession) {
|
||||
AssertionType assertion = response.getAssertions().get(0).getAssertion();
|
||||
public AttributeStatementType populateAttributeStatements(List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers, KeycloakSession session, UserSessionModel userSession,
|
||||
ClientSessionModel clientSession) {
|
||||
AttributeStatementType attributeStatement = new AttributeStatementType();
|
||||
|
||||
for (ProtocolMapperProcessor<SAMLAttributeStatementMapper> 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<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
|
@ -459,17 +461,11 @@ public class SamlProtocol implements LoginProtocol {
|
|||
return response;
|
||||
}
|
||||
|
||||
public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> 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) {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<description></description>
|
||||
|
||||
<properties>
|
||||
<exclude.test>-</exclude.test>
|
||||
<exclude.console>-</exclude.console>
|
||||
<exclude.account>-</exclude.account>
|
||||
<exclude.client>-</exclude.client>
|
||||
|
@ -97,6 +98,7 @@
|
|||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>${exclude.test}</exclude>
|
||||
<exclude>${exclude.console}</exclude>
|
||||
<exclude>${exclude.account}</exclude>
|
||||
<exclude>${exclude.client}</exclude>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -178,6 +178,12 @@ public class PasswordPolicyTest extends AbstractConsoleTest {
|
|||
|
||||
testUserCredentialsPage.resetPassword("firstPassword");
|
||||
assertAlertDanger();
|
||||
|
||||
testUserCredentialsPage.resetPassword("thirdPassword");
|
||||
assertAlertSuccess();
|
||||
|
||||
testUserCredentialsPage.resetPassword("firstPassword");
|
||||
assertAlertSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue