diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index d709fe257c..3e2b23e022 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -20,6 +20,7 @@ public class RealmRepresentation { protected boolean cookieLoginAllowed; protected boolean registrationAllowed; protected boolean verifyEmail; + protected boolean resetPasswordAllowed; protected boolean social; protected boolean automaticRegistrationAfterSocialLogin; protected String privateKey; @@ -248,6 +249,14 @@ public class RealmRepresentation { this.verifyEmail = verifyEmail; } + public boolean isResetPasswordAllowed() { + return resetPasswordAllowed; + } + + public void setResetPasswordAllowed(boolean resetPassword) { + this.resetPasswordAllowed = resetPassword; + } + public boolean isSocial() { return social; } diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index f23b917b1d..438ac68938 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -73,6 +73,7 @@ public class RealmManager { realm.setCookieLoginAllowed(rep.isCookieLoginAllowed()); realm.setRegistrationAllowed(rep.isRegistrationAllowed()); realm.setVerifyEmail(rep.isVerifyEmail()); + realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); realm.setAutomaticRegistrationAfterSocialLogin(rep.isAutomaticRegistrationAfterSocialLogin()); realm.setSslNotRequired((rep.isSslNotRequired())); realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); @@ -112,6 +113,7 @@ public class RealmManager { newRealm.setCookieLoginAllowed(rep.isCookieLoginAllowed()); newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); newRealm.setVerifyEmail(rep.isVerifyEmail()); + newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); newRealm.setAutomaticRegistrationAfterSocialLogin(rep.isAutomaticRegistrationAfterSocialLogin()); if (rep.getPrivateKey() == null || rep.getPublicKey() == null) { generateRealmKeys(newRealm); @@ -341,11 +343,23 @@ public class RealmManager { rep.setSslNotRequired(realm.isSslNotRequired()); rep.setCookieLoginAllowed(realm.isCookieLoginAllowed()); rep.setPublicKey(realm.getPublicKeyPem()); + rep.setPrivateKey(realm.getPrivateKeyPem()); rep.setRegistrationAllowed(realm.isRegistrationAllowed()); rep.setVerifyEmail(realm.isVerifyEmail()); + rep.setResetPasswordAllowed(realm.isResetPasswordAllowed()); rep.setTokenLifespan(realm.getTokenLifespan()); rep.setAccessCodeLifespan(realm.getAccessCodeLifespan()); rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction()); + + List defaultRoles = realm.getDefaultRoles(); + if (defaultRoles.size() > 0) { + String[] d = new String[defaultRoles.size()]; + for (int i = 0; i < d.length; i++) { + d[i] = defaultRoles.get(i).getName(); + } + rep.setDefaultRoles(d); + } + List requiredCredentialModels = realm.getRequiredCredentials(); if (requiredCredentialModels.size() > 0) { rep.setRequiredCredentials(new HashSet()); diff --git a/services/src/main/java/org/keycloak/services/models/RealmModel.java b/services/src/main/java/org/keycloak/services/models/RealmModel.java index 5deed17b83..192712062f 100755 --- a/services/src/main/java/org/keycloak/services/models/RealmModel.java +++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java @@ -39,6 +39,10 @@ public interface RealmModel { void setVerifyEmail(boolean verifyEmail); + boolean isResetPasswordAllowed(); + + void setResetPasswordAllowed(boolean resetPasswordAllowed); + int getTokenLifespan(); void setTokenLifespan(int tokenLifespan); diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java index 4fd3aa86be..7d5d87e276 100755 --- a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java +++ b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java @@ -177,6 +177,17 @@ public class RealmAdapter implements RealmModel { updateRealm(); } + @Override + public boolean isResetPasswordAllowed() { + return realm.isResetPasswordAllowed(); + } + + @Override + public void setResetPasswordAllowed(boolean resetPassword) { + realm.setResetPasswordAllowed(resetPassword); + updateRealm(); + } + @Override public int getTokenLifespan() { return realm.getTokenLifespan(); diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmData.java b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmData.java index 0e58cefaa9..cbf4ca5bda 100755 --- a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmData.java +++ b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmData.java @@ -14,6 +14,7 @@ public class RealmData extends AbstractPartition { private boolean cookieLoginAllowed; private boolean registrationAllowed; private boolean verifyEmail; + private boolean resetPasswordAllowed; private boolean social; private boolean automaticRegistrationAfterSocialLogin; private int tokenLifespan; @@ -101,6 +102,14 @@ public class RealmData extends AbstractPartition { this.verifyEmail = verifyEmail; } + public boolean isResetPasswordAllowed() { + return resetPasswordAllowed; + } + + public void setResetPasswordAllowed(boolean resetPassword) { + this.resetPasswordAllowed = resetPassword; + } + @AttributeProperty public int getTokenLifespan() { return tokenLifespan; diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmEntity.java b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmEntity.java index 9ddcd35e70..6e8d9bdf94 100755 --- a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmEntity.java +++ b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/RealmEntity.java @@ -37,6 +37,8 @@ public class RealmEntity implements Serializable { @AttributeValue private boolean verifyEmail; @AttributeValue + private boolean resetPasswordAllowed; + @AttributeValue private boolean social; @AttributeValue private boolean automaticRegistrationAfterSocialLogin; @@ -112,6 +114,14 @@ public class RealmEntity implements Serializable { this.verifyEmail = verifyEmail; } + public boolean isResetPasswordAllowed() { + return resetPasswordAllowed; + } + + public void setResetPasswordAllowed(boolean resetPassword) { + this.resetPasswordAllowed = resetPassword; + } + public boolean isSocial() { return social; } diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index e8160a27d7..3213fe3628 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -347,6 +347,10 @@ public class AccountService { if (!realm.isEnabled()) { return oauth.forwardToSecurityFailure("Realm not enabled."); } + if (!realm.isResetPasswordAllowed()) { + return oauth.forwardToSecurityFailure("Password reset not permitted, contact admin."); + } + UserModel client = realm.getUser(clientId); if (client == null) { return oauth.forwardToSecurityFailure("Unknown login requester."); diff --git a/services/src/test/java/org/keycloak/test/ModelTest.java b/services/src/test/java/org/keycloak/test/ModelTest.java new file mode 100644 index 0000000000..b69924af76 --- /dev/null +++ b/services/src/test/java/org/keycloak/test/ModelTest.java @@ -0,0 +1,88 @@ +package org.keycloak.test; + +import java.util.Iterator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.models.KeycloakSession; +import org.keycloak.services.models.KeycloakSessionFactory; +import org.keycloak.services.models.RealmModel; +import org.keycloak.services.models.RoleModel; +import org.keycloak.services.resources.KeycloakApplication; + +public class ModelTest extends AbstractKeycloakServerTest { + private KeycloakSessionFactory factory; + private KeycloakSession identitySession; + private RealmManager manager; + + @Before + public void before() throws Exception { + factory = KeycloakApplication.buildSessionFactory(); + identitySession = factory.createSession(); + identitySession.getTransaction().begin(); + manager = new RealmManager(identitySession); + } + + @Test + public void importExportRealm() { + RealmModel realm = manager.createRealm("original"); + realm.setCookieLoginAllowed(true); + realm.setRegistrationAllowed(true); + realm.setResetPasswordAllowed(true); + realm.setSocial(true); + realm.setSslNotRequired(true); + realm.setVerifyEmail(true); + realm.setTokenLifespan(1000); + realm.setAccessCodeLifespan(1001); + realm.setAccessCodeLifespanUserAction(1002); + realm.setPublicKeyPem("0234234"); + realm.setPrivateKeyPem("1234234"); + realm.addDefaultRole("default-role"); + + RealmModel peristed = manager.getRealm(realm.getId()); + assertEquals(realm, peristed); + + RealmModel copy = importExport(realm, "copy"); + assertEquals(realm, copy); + } + + public static void assertEquals(RealmModel expected, RealmModel actual) { + Assert.assertEquals(expected.isAutomaticRegistrationAfterSocialLogin(), + actual.isAutomaticRegistrationAfterSocialLogin()); + Assert.assertEquals(expected.isCookieLoginAllowed(), actual.isCookieLoginAllowed()); + Assert.assertEquals(expected.isRegistrationAllowed(), actual.isRegistrationAllowed()); + Assert.assertEquals(expected.isResetPasswordAllowed(), actual.isResetPasswordAllowed()); + Assert.assertEquals(expected.isSocial(), actual.isSocial()); + Assert.assertEquals(expected.isSslNotRequired(), actual.isSslNotRequired()); + Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail()); + Assert.assertEquals(expected.getTokenLifespan(), actual.getTokenLifespan()); + + Assert.assertEquals(expected.getAccessCodeLifespan(), actual.getAccessCodeLifespan()); + Assert.assertEquals(expected.getAccessCodeLifespanUserAction(), actual.getAccessCodeLifespanUserAction()); + Assert.assertEquals(expected.getPublicKeyPem(), actual.getPublicKeyPem()); + Assert.assertEquals(expected.getPrivateKeyPem(), actual.getPrivateKeyPem()); + + assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles()); + } + + public static void assertEquals(List expected, List actual) { + Assert.assertEquals(expected.size(), actual.size()); + Iterator exp = expected.iterator(); + Iterator act = actual.iterator(); + while (exp.hasNext()) { + Assert.assertEquals(exp.next().getName(), act.next().getName()); + } + } + + private RealmModel importExport(RealmModel src, String copyName) { + RealmRepresentation representation = manager.toRepresentation(src); + RealmModel copy = manager.createRealm(copyName); + manager.importRealm(representation, copy); + return manager.getRealm(copy.getId()); + } + +} diff --git a/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java b/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java index 53b859caf9..e90a1d9c42 100644 --- a/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java +++ b/testsuite/src/test/java/org/keycloak/testsuite/ResetPasswordTest.java @@ -61,12 +61,15 @@ public class ResetPasswordTest extends AbstractDroneTest { Assert.assertTrue(loginPage.isCurrent()); + // TODO Replace with clicking reset password link when added String url = browser.getCurrentUrl(); url = url.replace("tokens/login", "account/password-reset"); url = url + "&username=bburke@redhat.com"; browser.navigate().to(url); + Assert.assertEquals(1, greenMail.getReceivedMessages().length); + MimeMessage message = greenMail.getReceivedMessages()[0]; String body = (String) message.getContent(); diff --git a/testsuite/src/test/resources/testrealm.json b/testsuite/src/test/resources/testrealm.json index 2ef8cebb55..3161c1f285 100755 --- a/testsuite/src/test/resources/testrealm.json +++ b/testsuite/src/test/resources/testrealm.json @@ -7,6 +7,7 @@ "sslNotRequired": true, "cookieLoginAllowed": true, "registrationAllowed": true, + "resetPasswordAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ],