diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html index 2628dcca1b..7975742bbf 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html @@ -46,8 +46,8 @@
- - + +
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 45ec844664..376de43a5e 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -23,7 +23,7 @@ public class RealmRepresentation { protected Boolean verifyEmail; protected Boolean resetPasswordAllowed; protected Boolean social; - protected Boolean automaticRegistrationAfterSocialLogin; + protected Boolean updateProfileOnInitialSocialLogin; protected String privateKey; protected String publicKey; protected List roles; @@ -279,12 +279,12 @@ public class RealmRepresentation { this.social = social; } - public Boolean isAutomaticRegistrationAfterSocialLogin() { - return automaticRegistrationAfterSocialLogin; + public Boolean isUpdateProfileOnInitialSocialLogin() { + return updateProfileOnInitialSocialLogin; } - public void setAutomaticRegistrationAfterSocialLogin(Boolean automaticRegistrationAfterSocialLogin) { - this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin; + public void setUpdateProfileOnInitialSocialLogin(Boolean updateProfileOnInitialSocialLogin) { + this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; } public Map getSocialProviders() { diff --git a/examples/as7-eap-demo/testrealm.json b/examples/as7-eap-demo/testrealm.json index f9354df4c8..da8b804b03 100755 --- a/examples/as7-eap-demo/testrealm.json +++ b/examples/as7-eap-demo/testrealm.json @@ -8,7 +8,7 @@ "sslNotRequired": true, "registrationAllowed": false, "social": false, - "automaticRegistrationAfterSocialLogin": false, + "updateProfileOnInitialSocialLogin": false, "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" ], diff --git a/examples/wildfly-demo/testrealm.json b/examples/wildfly-demo/testrealm.json index f9354df4c8..da8b804b03 100755 --- a/examples/wildfly-demo/testrealm.json +++ b/examples/wildfly-demo/testrealm.json @@ -8,7 +8,7 @@ "sslNotRequired": true, "registrationAllowed": false, "social": false, - "automaticRegistrationAfterSocialLogin": false, + "updateProfileOnInitialSocialLogin": false, "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" ], diff --git a/forms/src/main/java/org/keycloak/forms/RegisterBean.java b/forms/src/main/java/org/keycloak/forms/RegisterBean.java index 94bb83cc66..d801a42205 100755 --- a/forms/src/main/java/org/keycloak/forms/RegisterBean.java +++ b/forms/src/main/java/org/keycloak/forms/RegisterBean.java @@ -32,14 +32,10 @@ public class RegisterBean { private Map formData = new HashMap(); - private boolean socialRegistration; - - public RegisterBean(MultivaluedMap formData, boolean socialRegistration) { + public RegisterBean(MultivaluedMap formData) { this.formData = new HashMap(); - this.socialRegistration = socialRegistration; - if (formData != null) { for (String k : formData.keySet()) { this.formData.put(k, formData.getFirst(k)); @@ -51,8 +47,4 @@ public class RegisterBean { return formData; } - public boolean isSocialRegistration() { - return socialRegistration; - } - } diff --git a/forms/src/main/java/org/keycloak/forms/SocialBean.java b/forms/src/main/java/org/keycloak/forms/SocialBean.java index 0861b55404..939aa2953d 100755 --- a/forms/src/main/java/org/keycloak/forms/SocialBean.java +++ b/forms/src/main/java/org/keycloak/forms/SocialBean.java @@ -64,7 +64,7 @@ public class SocialBean { // Display panel with social providers just in case that social is enabled for realm, but we are not in the middle of registration with social public boolean isDisplaySocialProviders() { - return realm.isSocial() && !providers.isEmpty() && !registerBean.isSocialRegistration(); + return realm.isSocial() && !providers.isEmpty(); } public RealmBean getRealm() { diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java index c3e24b119a..4637cc200a 100755 --- a/forms/src/main/java/org/keycloak/forms/UrlBean.java +++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java @@ -34,18 +34,8 @@ public class UrlBean { private RealmBean realm; - private boolean socialRegistration; - private String referrerURI; - public boolean isSocialRegistration() { - return socialRegistration; - } - - public void setSocialRegistration(boolean socialRegistration) { - this.socialRegistration = socialRegistration; - } - public UrlBean(RealmBean realm, URI baseURI, String referrerURI){ this.realm = realm; this.baseURI = baseURI; @@ -91,8 +81,6 @@ public class UrlBean { public String getRegistrationAction() { if (realm.isSaas()) { return Urls.saasRegisterAction(baseURI).toString(); - } else if (socialRegistration){ - return Urls.socialRegisterAction(baseURI, getRealmIdentifier()).toString(); } else { return Urls.realmRegisterAction(baseURI, getRealmIdentifier()).toString(); } diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java index 33d36ea51e..128bb9795a 100755 --- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java +++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java @@ -140,7 +140,6 @@ public class FormServiceImpl implements FormService { } url = new UrlBean(realm, dataBean.getBaseURI(), referrerUri); - url.setSocialRegistration(dataBean.getSocialRegistration()); user = new UserBean(dataBean.getUserModel()); login = new LoginBean(realm, dataBean.getFormData()); @@ -163,7 +162,7 @@ public class FormServiceImpl implements FormService { public void exec(Map attributes, FormServiceDataBean dataBean) { super.exec(attributes, dataBean); - RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration()); + RegisterBean register = new RegisterBean(dataBean.getFormData()); SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url); attributes.put("social", social); } @@ -173,7 +172,7 @@ public class FormServiceImpl implements FormService { public void exec(Map attributes, FormServiceDataBean dataBean) { super.exec(attributes, dataBean); - RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration()); + RegisterBean register = new RegisterBean(dataBean.getFormData()); SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url); attributes.put("social", social); } @@ -183,7 +182,7 @@ public class FormServiceImpl implements FormService { public void exec(Map attributes, FormServiceDataBean dataBean) { super.exec(attributes, dataBean); - RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration()); + RegisterBean register = new RegisterBean(dataBean.getFormData()); attributes.put("register", register); SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url); diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java index 42ab0e684a..184e6aafa7 100755 --- a/model/api/src/main/java/org/keycloak/models/RealmModel.java +++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java @@ -132,9 +132,9 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa void setSocial(boolean social); - public boolean isAutomaticRegistrationAfterSocialLogin(); + public boolean isUpdateProfileOnInitialSocialLogin(); - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin); + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin); List getUsers(); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index d08ed73b53..0488bddd81 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -663,13 +663,13 @@ public class RealmAdapter implements RealmModel { } @Override - public boolean isAutomaticRegistrationAfterSocialLogin() { - return realm.isAutomaticRegistrationAfterSocialLogin(); + public boolean isUpdateProfileOnInitialSocialLogin() { + return realm.isUpdateProfileOnInitialSocialLogin(); } @Override - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { - realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin); + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) { + realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin); em.flush(); } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java index 6f4fadf3a2..067b8dcdfd 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java @@ -38,7 +38,7 @@ public class RealmEntity { protected boolean verifyEmail; protected boolean resetPasswordAllowed; protected boolean social; - protected boolean automaticRegistrationAfterSocialLogin; + protected boolean updateProfileOnInitialSocialLogin; protected String passwordPolicy; protected int tokenLifespan; @@ -149,12 +149,12 @@ public class RealmEntity { this.social = social; } - public boolean isAutomaticRegistrationAfterSocialLogin() { - return automaticRegistrationAfterSocialLogin; + public boolean isUpdateProfileOnInitialSocialLogin() { + return updateProfileOnInitialSocialLogin; } - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { - this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin; + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) { + this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; } public int getTokenLifespan() { diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java index 0fb2c22a0f..13d78a35ed 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java @@ -125,13 +125,13 @@ public class RealmAdapter implements RealmModel { } @Override - public boolean isAutomaticRegistrationAfterSocialLogin() { - return realm.isAutomaticRegistrationAfterSocialLogin(); + public boolean isUpdateProfileOnInitialSocialLogin() { + return realm.isUpdateProfileOnInitialSocialLogin(); } @Override - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { - realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin); + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) { + realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin); updateRealm(); } diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java index 2914cbcc8d..03c346665d 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmData.java @@ -17,7 +17,7 @@ public class RealmData extends AbstractPartition { private boolean verifyEmail; private boolean resetPasswordAllowed; private boolean social; - private boolean automaticRegistrationAfterSocialLogin; + private boolean updateProfileOnInitialSocialLogin; private int tokenLifespan; private int accessCodeLifespan; private int accessCodeLifespanUserAction; @@ -63,12 +63,12 @@ public class RealmData extends AbstractPartition { } @AttributeProperty - public boolean isAutomaticRegistrationAfterSocialLogin() { - return automaticRegistrationAfterSocialLogin; + public boolean isUpdateProfileOnInitialSocialLogin() { + return updateProfileOnInitialSocialLogin; } - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { - this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin; + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) { + this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; } @AttributeProperty diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java index 21913c7f4d..fe0a448fbd 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/RealmEntity.java @@ -41,7 +41,7 @@ public class RealmEntity implements Serializable { @AttributeValue private boolean social; @AttributeValue - private boolean automaticRegistrationAfterSocialLogin; + private boolean updateProfileOnInitialSocialLogin; @AttributeValue private int tokenLifespan; @AttributeValue @@ -128,12 +128,12 @@ public class RealmEntity implements Serializable { this.social = social; } - public boolean isAutomaticRegistrationAfterSocialLogin() { - return automaticRegistrationAfterSocialLogin; + public boolean isUpdateProfileOnInitialSocialLogin() { + return updateProfileOnInitialSocialLogin; } - public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) { - this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin; + public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) { + this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin; } public int getTokenLifespan() { diff --git a/pom.xml b/pom.xml index 48246591d2..91fd676291 100755 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ 1.6.1 7.1.1.Final 8.0.0.CR1 + 20131018 http://keycloak.org @@ -245,6 +246,12 @@ freemarker 2.3.19 + + + org.json + json + ${json.version} + diff --git a/services/src/main/java/org/keycloak/services/FormService.java b/services/src/main/java/org/keycloak/services/FormService.java index f525bbd6f4..74629004e5 100755 --- a/services/src/main/java/org/keycloak/services/FormService.java +++ b/services/src/main/java/org/keycloak/services/FormService.java @@ -57,16 +57,6 @@ public interface FormService { private List socialProviders; - public Boolean getSocialRegistration() { - return socialRegistration; - } - - public void setSocialRegistration(Boolean socialRegistration) { - this.socialRegistration = socialRegistration; - } - - private Boolean socialRegistration; - public String getCode() { return code; } 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 0cd9fe3951..7f1ead8d42 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -102,8 +102,8 @@ public class RealmManager { if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isAutomaticRegistrationAfterSocialLogin() != null) - realm.setAutomaticRegistrationAfterSocialLogin(rep.isAutomaticRegistrationAfterSocialLogin()); + if (rep.isUpdateProfileOnInitialSocialLogin() != null) + realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); if (rep.isSslNotRequired() != null) realm.setSslNotRequired((rep.isSslNotRequired())); if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); if (rep.getAccessCodeLifespanUserAction() != null) @@ -200,8 +200,8 @@ public class RealmManager { if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail()); if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed()); - if (rep.isAutomaticRegistrationAfterSocialLogin() != null) - newRealm.setAutomaticRegistrationAfterSocialLogin(rep.isAutomaticRegistrationAfterSocialLogin()); + if (rep.isUpdateProfileOnInitialSocialLogin() != null) + newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin()); if (rep.getPrivateKey() == null || rep.getPublicKey() == null) { generateRealmKeys(newRealm); } else { @@ -475,7 +475,7 @@ public class RealmManager { rep.setRealm(realm.getName()); rep.setEnabled(realm.isEnabled()); rep.setSocial(realm.isSocial()); - rep.setAutomaticRegistrationAfterSocialLogin(realm.isAutomaticRegistrationAfterSocialLogin()); + rep.setUpdateProfileOnInitialSocialLogin(realm.isUpdateProfileOnInitialSocialLogin()); rep.setSslNotRequired(realm.isSslNotRequired()); rep.setPublicKey(realm.getPublicKeyPem()); rep.setPrivateKey(realm.getPrivateKeyPem()); diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java index 83fa595ea9..770d38641d 100755 --- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -143,7 +143,7 @@ public class SocialResource { AuthCallback callback = new AuthCallback(requestData.getSocialAttributes(), queryParams); - SocialUser socialUser = null; + SocialUser socialUser; try { socialUser = provider.processCallback(config, callback); } catch (SocialProviderException e) { @@ -160,41 +160,26 @@ public class SocialResource { } // Automatically register user into realm with his social username (don't redirect to registration screen) - if (realm.isAutomaticRegistrationAfterSocialLogin()) { + if (realm.getUser(socialUser.getUsername()) != null) { + // TODO: Username is already in realm. Show message and let user to bind accounts after he re-authenticate + throw new IllegalStateException("Username " + socialUser.getUsername() + + " already registered in the realm. TODO: bind accounts..."); - if (realm.getUser(socialUser.getUsername()) != null) { - // TODO: Username is already in realm. Show message and let user to bind accounts after he re-authenticate - throw new IllegalStateException("Username " + socialUser.getUsername() + - " already registered in the realm. TODO: bind accounts..."); - - // TODO: Maybe we should search also by email and bind accounts if user with this email is - // already registered. But actually Keycloak allows duplicate emails - } else { - user = realm.addUser(socialUser.getUsername()); - user.setEnabled(true); - user.setFirstName(socialUser.getFirstName()); - user.setLastName(socialUser.getLastName()); - user.setEmail(socialUser.getEmail()); - } - - realm.addSocialLink(user, socialLink); + // TODO: Maybe we should search also by email and bind accounts if user with this email is + // already registered. But actually Keycloak allows duplicate emails } else { - // Redirect user to registration screen with prefilled data from social provider - MultivaluedMap formData = fillRegistrationFormWithSocialData(socialUser); + user = realm.addUser(socialUser.getUsername()); + user.setEnabled(true); + user.setFirstName(socialUser.getFirstName()); + user.setLastName(socialUser.getLastName()); + user.setEmail(socialUser.getEmail()); - String requestId = UUID.randomUUID().toString(); - socialRequestManager.addRequest(requestId, RequestDetails.create(requestData).build()); - boolean secureOnly = !realm.isSslNotRequired(); - String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath(); - logger.debug("creating cookie for social registration - name: {0} path: {1}", SocialConstants.SOCIAL_REGISTRATION_COOKIE, - cookiePath); - NewCookie newCookie = new NewCookie(SocialConstants.SOCIAL_REGISTRATION_COOKIE, requestId, - cookiePath, null, "Added social cookie", NewCookie.DEFAULT_MAX_AGE, secureOnly); - response.addNewCookie(newCookie); - - return Flows.forms(realm, request, uriInfo).setFormData(formData).setSocialRegistration(true) - .forwardToRegistration(); + if (realm.isUpdateProfileOnInitialSocialLogin()) { + user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE); + } } + + realm.addSocialLink(user, socialLink); } if (!user.isEnabled()) { @@ -213,7 +198,7 @@ public class SocialResource { public Response redirectToProviderAuth(@PathParam("realm") final String realmId, @QueryParam("provider_id") final String providerId, @QueryParam("client_id") final String clientId, @QueryParam("scope") final String scope, @QueryParam("state") final String state, - @QueryParam("redirect_uri") final String redirectUri) { + @QueryParam("redirect_uri") String redirectUri) { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.getRealm(realmId); @@ -228,6 +213,21 @@ public class SocialResource { SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); + UserModel client = realm.getUser(clientId); + if (client == null) { + logger.warn("Unknown login requester: " + clientId); + return Flows.forms(realm, request, uriInfo).setError("Unknown login requester.").forwardToErrorPage(); + } + + if (!client.isEnabled()) { + logger.warn("Login requester not enabled."); + return Flows.forms(realm, request, uriInfo).setError("Login requester not enabled.").forwardToErrorPage(); + } + redirectUri = TokenService.verifyRedirectUri(redirectUri, client); + if (redirectUri == null) { + return Flows.forms(realm, request, uriInfo).setError("Invalid redirect_uri.").forwardToErrorPage(); + } + try { AuthRequest authRequest = provider.getAuthUrl(config); @@ -244,65 +244,6 @@ public class SocialResource { } } - @POST - @Path("{realm}/socialRegistration") - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response socialRegistration(@PathParam("realm") final String realmId, - final MultivaluedMap formData) { - RealmManager realmManager = new RealmManager(session); - RealmModel realm = realmManager.getRealm(realmId); - - Cookie cookie = headers.getCookies().get(SocialConstants.SOCIAL_REGISTRATION_COOKIE); - if (cookie == null) { - return Flows.forms(realm, request, uriInfo).setError("Social registration cookie not found").forwardToErrorPage(); - } - - String requestId = cookie.getValue(); - if (!socialRequestManager.isRequestId(requestId)) { - logger.error("Unknown requestId found in cookie. Maybe it's expired. requestId=" + requestId); - return Flows.forms(realm, request, uriInfo).setError("Unknown requestId found in cookie. Maybe it's expired.").forwardToErrorPage(); - } - - RequestDetails requestData = socialRequestManager.getData(requestId); - - if (realm == null || !realm.isEnabled()) { - return Flows.forms(realm, request, uriInfo).setError("Realm doesn't exists or is not enabled.").forwardToErrorPage(); - } - TokenService tokenService = new TokenService(realm, tokenManager); - resourceContext.initResource(tokenService); - - String clientId = requestData.getClientAttribute("clientId"); - String scope = requestData.getClientAttribute("scope"); - String state = requestData.getClientAttribute("state"); - String redirectUri = requestData.getClientAttribute("redirectUri"); - - Response response1 = tokenService.processRegisterImpl(clientId, scope, state, redirectUri, formData, true); - - // Some error occured during registration - if (response1 != null || request.wasForwarded()) { - logger.warn("Registration attempt wasn't successful. Request already forwarded or redirected."); - return response1; - } - - String username = formData.getFirst("username"); - UserModel user = realm.getUser(username); - if (user == null) { - // Normally shouldn't happen - throw new IllegalStateException("User " + username + " not found in the realm"); - } - realm.addSocialLink(user, new SocialLinkModel(requestData.getProviderId(), username)); - - // Expire cookie and invalidate requestData - String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath(); - NewCookie newCookie = new NewCookie(SocialConstants.SOCIAL_REGISTRATION_COOKIE, "", cookiePath, null, - "Expire social cookie", 0, false); - logger.debug("Expiring social registration cookie: {0}, path: {1}", SocialConstants.SOCIAL_REGISTRATION_COOKIE, cookiePath); - response.addNewCookie(newCookie); - socialRequestManager.retrieveData(requestId); - - return tokenService.processLogin(clientId, scope, state, redirectUri, formData); - } - private RequestDetails getRequestDetails(Map queryParams) { for (SocialProvider provider : SocialLoader.load()) { if (queryParams.containsKey(provider.getRequestIdParamName())) { @@ -324,25 +265,4 @@ public class SocialResource { return queryParams; } - protected MultivaluedMap fillRegistrationFormWithSocialData(SocialUser socialUser) { - MultivaluedMap formData = new MultivaluedMapImpl(); - formData.putSingle("username", socialUser.getUsername()); - - if (socialUser.getEmail() != null) { - formData.putSingle("email", socialUser.getEmail()); - } - - String fullName = null; - if (socialUser.getFirstName() == null) { - fullName = socialUser.getLastName(); - } else if (socialUser.getLastName() == null) { - fullName = socialUser.getFirstName(); - } else { - fullName = socialUser.getFirstName() + " " + socialUser.getLastName(); - } - - formData.putSingle("name", fullName); - return formData; - } - } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index 36bd3f4554..24c014cdc6 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -276,20 +276,7 @@ public class TokenService { @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response processRegister(@QueryParam("client_id") final String clientId, @QueryParam("scope") final String scopeParam, @QueryParam("state") final String state, - @QueryParam("redirect_uri") final String redirect, final MultivaluedMap formData) { - Response registrationResponse = processRegisterImpl(clientId, scopeParam, state, redirect, formData, false); - - // If request has been already forwarded (either due to security or validation error) then we won't continue with login - if (registrationResponse != null || request.wasForwarded()) { - logger.warn("Registration attempt wasn't successful. Request already forwarded or redirected."); - return registrationResponse; - } else { - return processLogin(clientId, scopeParam, state, redirect, formData); - } - } - - public Response processRegisterImpl(String clientId, String scopeParam, String state, String redirect, - MultivaluedMap formData, boolean isSocialRegistration) { + @QueryParam("redirect_uri") String redirect, final MultivaluedMap formData) { OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); if (!realm.isEnabled()) { @@ -328,16 +315,14 @@ public class TokenService { } if (error != null) { - return Flows.forms(realm, request, uriInfo).setError(error).setFormData(formData) - .setSocialRegistration(isSocialRegistration).forwardToRegistration(); + return Flows.forms(realm, request, uriInfo).setError(error).setFormData(formData).forwardToRegistration(); } String username = formData.getFirst("username"); UserModel user = realm.getUser(username); if (user != null) { - return Flows.forms(realm, request, uriInfo).setError(Messages.USERNAME_EXISTS).setFormData(formData) - .setSocialRegistration(isSocialRegistration).forwardToRegistration(); + return Flows.forms(realm, request, uriInfo).setError(Messages.USERNAME_EXISTS).setFormData(formData).forwardToRegistration(); } user = realm.addUser(username); @@ -364,8 +349,7 @@ public class TokenService { } } - - return null; + return processLogin(clientId, scopeParam, state, redirect, formData); } @Path("access/codes") @@ -647,7 +631,7 @@ public class TokenService { return location.build(); } - protected String verifyRedirectUri(String redirectUri, UserModel client) { + public static String verifyRedirectUri(String redirectUri, UserModel client) { if (redirectUri == null) { return client.getRedirectUris().size() == 1 ? client.getRedirectUris().iterator().next() : null; } else if (client.getRedirectUris().isEmpty()) { diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java index 5657ecf22c..d92e40326e 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java @@ -66,7 +66,6 @@ public class FormFlows { private HttpRequest request; private UserModel userModel; - private boolean socialRegistration; private AccessCodeEntry accessCode; private UriInfo uriInfo; @@ -119,7 +118,7 @@ public class FormFlows { } if (accessCode != null) { - uriBuilder.queryParam(CODE, accessCode.getCode()); + uriBuilder.replaceQueryParam(CODE, accessCode.getCode()); } if (queryParams != null) { @@ -134,7 +133,6 @@ public class FormFlows { // TODO find a better way to obtain contextPath // Getting context path by removing "rest/" substring from the BaseUri path formDataBean.setContextPath(requestURI.substring(0, requestURI.length() - 6)); - formDataBean.setSocialRegistration(socialRegistration); // Find the service and process relevant template Iterator itr = ServiceLoader.load(FormService.class).iterator(); @@ -261,12 +259,6 @@ public class FormFlows { return this; } - // Set flag whether user registration is triggered from social login - public FormFlows setSocialRegistration(boolean socialRegistration) { - this.socialRegistration = socialRegistration; - return this; - } - public FormFlows setFormData(MultivaluedMap formData) { this.formData = formData; return this; diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java index 042efaffde..7859c78595 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java @@ -165,10 +165,6 @@ public class Urls { .build(realmId); } - public static URI socialRegisterAction(URI baseUri, String realmId) { - return socialBase(baseUri).path(SocialResource.class, "socialRegistration").build(realmId); - } - private static UriBuilder requiredActionsBase(URI baseUri) { return tokenBase(baseUri).path(TokenService.class, "getRequiredActionsService"); } diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java index 0fc412fb9c..cd522d8f39 100755 --- a/services/src/test/java/org/keycloak/test/AdapterTest.java +++ b/services/src/test/java/org/keycloak/test/AdapterTest.java @@ -65,7 +65,7 @@ public class AdapterTest extends AbstractKeycloakTest { realmModel.setPrivateKeyPem("0234234"); realmModel.setPublicKeyPem("0234234"); realmModel.setTokenLifespan(1000); - realmModel.setAutomaticRegistrationAfterSocialLogin(true); + realmModel.setUpdateProfileOnInitialSocialLogin(true); realmModel.addDefaultRole("foo"); System.out.println(realmModel.getId()); @@ -78,7 +78,7 @@ public class AdapterTest extends AbstractKeycloakTest { Assert.assertEquals(realmModel.getName(), "JUGGLER"); Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); - Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true); + Assert.assertEquals(realmModel.isUpdateProfileOnInitialSocialLogin(), true); Assert.assertEquals(1, realmModel.getDefaultRoles().size()); Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0)); } @@ -93,7 +93,7 @@ public class AdapterTest extends AbstractKeycloakTest { realmModel.setPrivateKeyPem("0234234"); realmModel.setPublicKeyPem("0234234"); realmModel.setTokenLifespan(1000); - realmModel.setAutomaticRegistrationAfterSocialLogin(true); + realmModel.setUpdateProfileOnInitialSocialLogin(true); realmModel.addDefaultRole("foo"); System.out.println(realmModel.getId()); @@ -106,7 +106,7 @@ public class AdapterTest extends AbstractKeycloakTest { Assert.assertEquals(realmModel.getName(), "JUGGLER"); Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); - Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true); + Assert.assertEquals(realmModel.isUpdateProfileOnInitialSocialLogin(), true); Assert.assertEquals(1, realmModel.getDefaultRoles().size()); Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0)); diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java index 2645f6d2a5..5c4550964c 100755 --- a/services/src/test/java/org/keycloak/test/ImportTest.java +++ b/services/src/test/java/org/keycloak/test/ImportTest.java @@ -37,7 +37,7 @@ public class ImportTest extends AbstractKeycloakTest { Assert.assertTrue(realm.isVerifyEmail()); - Assert.assertFalse(realm.isAutomaticRegistrationAfterSocialLogin()); + Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin()); List creds = realm.getRequiredCredentials(); Assert.assertEquals(1, creds.size()); RequiredCredentialModel cred = creds.get(0); @@ -94,7 +94,7 @@ public class ImportTest extends AbstractKeycloakTest { RealmModel realm = manager.createRealm("demo", rep.getRealm()); manager.importRealm(rep, realm); - Assert.assertTrue(realm.isAutomaticRegistrationAfterSocialLogin()); + Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin()); Assert.assertEquals(600, realm.getAccessCodeLifespanUserAction()); verifyRequiredCredentials(realm.getRequiredCredentials(), "password"); verifyRequiredCredentials(realm.getRequiredApplicationCredentials(), "totp"); diff --git a/services/src/test/java/org/keycloak/test/ModelTest.java b/services/src/test/java/org/keycloak/test/ModelTest.java index 35ea589728..32a428bc85 100755 --- a/services/src/test/java/org/keycloak/test/ModelTest.java +++ b/services/src/test/java/org/keycloak/test/ModelTest.java @@ -71,8 +71,8 @@ public class ModelTest extends AbstractKeycloakServerTest { } public static void assertEquals(RealmModel expected, RealmModel actual) { - Assert.assertEquals(expected.isAutomaticRegistrationAfterSocialLogin(), - actual.isAutomaticRegistrationAfterSocialLogin()); + Assert.assertEquals(expected.isUpdateProfileOnInitialSocialLogin(), + actual.isUpdateProfileOnInitialSocialLogin()); Assert.assertEquals(expected.isRegistrationAllowed(), actual.isRegistrationAllowed()); Assert.assertEquals(expected.isResetPasswordAllowed(), actual.isResetPasswordAllowed()); Assert.assertEquals(expected.isSocial(), actual.isSocial()); diff --git a/services/src/test/resources/testrealm-demo.json b/services/src/test/resources/testrealm-demo.json index fab9e9d364..753dfb0780 100755 --- a/services/src/test/resources/testrealm-demo.json +++ b/services/src/test/resources/testrealm-demo.json @@ -5,7 +5,7 @@ "accessCodeLifespan": 10, "accessCodeLifespanUserAction": 600, "sslNotRequired": true, - "automaticRegistrationAfterSocialLogin": true, + "updateProfileOnInitialSocialLogin": false, "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" ], diff --git a/social/core/pom.xml b/social/core/pom.xml index 47a694d180..473d1c0b53 100755 --- a/social/core/pom.xml +++ b/social/core/pom.xml @@ -14,9 +14,23 @@ - org.jboss.resteasy - jaxrs-api - provided + org.json + json + + + junit + junit + test + + + io.undertow + undertow-servlet + test + + + io.undertow + undertow-core + test diff --git a/social/core/src/main/java/org/keycloak/social/AuthRequest.java b/social/core/src/main/java/org/keycloak/social/AuthRequest.java index 481885dacd..9b213b0f05 100644 --- a/social/core/src/main/java/org/keycloak/social/AuthRequest.java +++ b/social/core/src/main/java/org/keycloak/social/AuthRequest.java @@ -21,8 +21,10 @@ */ package org.keycloak.social; -import javax.ws.rs.core.UriBuilder; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; @@ -40,7 +42,10 @@ public class AuthRequest { public static AuthRequestBuilder create(String id, String path) { AuthRequestBuilder req = new AuthRequestBuilder(); req.id = id; - req.b = UriBuilder.fromUri(path); + + req.b = new StringBuilder(); + req.b.append(path); + req.attributes = new HashMap(); return req; } @@ -65,18 +70,33 @@ public class AuthRequest { public static class AuthRequestBuilder { - private UriBuilder b; + private StringBuilder b; + + private char sep; private Map attributes; private String id; private AuthRequestBuilder() { + sep = '?'; } public AuthRequestBuilder setQueryParam(String name, String value) { - b.queryParam(name, value); - return this; + try { + if (sep == '?') { + b.append(sep); + sep = '&'; + } else { + b.append(sep); + } + b.append(URLEncoder.encode(name, "UTF-8")); + b.append("="); + b.append(URLEncoder.encode(value, "UTF-8")); + return this; + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } } public AuthRequestBuilder setAttribute(String name, String value) { @@ -85,7 +105,11 @@ public class AuthRequest { } public AuthRequest build() { - return new AuthRequest(id, b.build(), attributes); + try { + return new AuthRequest(id, new URI(b.toString()), attributes); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } } } diff --git a/social/core/src/main/java/org/keycloak/social/utils/SimpleHttp.java b/social/core/src/main/java/org/keycloak/social/utils/SimpleHttp.java new file mode 100644 index 0000000000..d5ef204f07 --- /dev/null +++ b/social/core/src/main/java/org/keycloak/social/utils/SimpleHttp.java @@ -0,0 +1,142 @@ +package org.keycloak.social.utils; + +import org.json.JSONObject; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Stian Thorgersen + */ +public class SimpleHttp { + + private String url; + private String method; + private Map headers; + private Map params; + + private SimpleHttp(String url, String method) { + this.url = url; + this.method = method; + } + + public static SimpleHttp doGet(String url) { + return new SimpleHttp(url, "GET"); + } + + public static SimpleHttp doPost(String url) { + return new SimpleHttp(url, "POST"); + } + + public SimpleHttp header(String name, String value) { + if (headers == null) { + headers = new HashMap(); + } + headers.put(name, value); + return this; + } + + public SimpleHttp param(String name, String value) { + if (params == null) { + params = new HashMap(); + } + params.put(name, value); + return this; + } + + public JSONObject asJson() throws IOException { + return new JSONObject(asString()); + } + + public String asString() throws IOException { + boolean get = method.equals("GET"); + boolean post = method.equals("POST"); + + StringBuilder sb = new StringBuilder(); + if (get) { + sb.append(url); + } + + if (params != null) { + boolean f = true; + for (Map.Entry p : params.entrySet()) { + if (f) { + f = false; + if (get) { + sb.append("?"); + } + } else { + sb.append("&"); + } + sb.append(URLEncoder.encode(p.getKey(), "UTF-8")); + sb.append("="); + sb.append(URLEncoder.encode(p.getValue(), "UTF-8")); + } + } + + if (get) { + url = sb.toString(); + } + + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + OutputStream os = null; + InputStream is = null; + + try { + connection.setRequestMethod(method); + + if (headers != null) { + for (Map.Entry h : headers.entrySet()) { + connection.setRequestProperty(h.getKey(), h.getValue()); + } + } + + if (post) { + String data = sb.toString(); + + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + connection.setRequestProperty("Content-Length", String.valueOf(data.length())); + + os = connection.getOutputStream(); + os.write(data.getBytes()); + } else { + connection.setDoOutput(false); + } + + is = connection.getInputStream(); + return toString(is); + } finally { + if (os != null) { + os.close(); + } + + if (is != null) { + is.close(); + } + } + } + + private String toString(InputStream is) throws IOException { + InputStreamReader reader = new InputStreamReader(is); + + StringWriter writer = new StringWriter(); + + char[] buffer = new char[1024 * 4]; + for (int n = reader.read(buffer); n != -1; n = reader.read(buffer)) { + writer.write(buffer, 0, n); + } + + return writer.toString(); + } + +} diff --git a/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java b/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java new file mode 100644 index 0000000000..dea58c7f1d --- /dev/null +++ b/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java @@ -0,0 +1,100 @@ +package org.keycloak.social.utils; + +import io.undertow.servlet.api.DeploymentInfo; +import io.undertow.servlet.api.ServletInfo; +import org.json.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static io.undertow.servlet.Servlets.servlet; +import static org.junit.Assert.assertEquals; + +/** + * @author Stian Thorgersen + */ +public class SimpleHttpTest { + + private UndertowServer server; + + @Before + public void before() { + server = new UndertowServer("localhost", 8081); + + DeploymentInfo deploymentInfo = new DeploymentInfo(); + deploymentInfo.setClassLoader(getClass().getClassLoader()); + deploymentInfo.setDeploymentName("test"); + deploymentInfo.setContextPath("/"); + + ServletInfo servlet = servlet("ToJsonServlet", ToJsonServlet.class) + .addMapping("/tojson"); + + deploymentInfo.addServlet(servlet); + + server.deploy(deploymentInfo); + + server.start(); + } + + @After + public void after() { + server.stop(); + } + + @Test + public void testPostNoParams() throws IOException { + JSONObject o = SimpleHttp.doPost("http://localhost:8081/tojson").asJson(); + JSONObject p = o.getJSONObject("params"); + + assertEquals(0, p.length()); + } + + @Test + public void testPost() throws IOException { + JSONObject o = SimpleHttp.doPost("http://localhost:8081/tojson").param("key-one", "value one ;)").param("key-two", "value two!&").asJson(); + JSONObject p = o.getJSONObject("params"); + + assertEquals(2, p.length()); + assertEquals("value one ;)", p.getString("key-one")); + assertEquals("value two!&", p.getString("key-two")); + } + + @Test + public void testPostCustomHeader() throws IOException { + JSONObject o = SimpleHttp.doPost("http://localhost:8081/tojson").header("Accept", "application/json").header("Authorization", "bearer dsfsadfsdf").asJson(); + JSONObject h = o.getJSONObject("headers"); + + assertEquals("application/json", h.getString("Accept")); + assertEquals("bearer dsfsadfsdf", h.getString("Authorization")); + } + + @Test + public void testGetNoParams() throws IOException { + JSONObject o = SimpleHttp.doGet("http://localhost:8081/tojson").asJson(); + JSONObject p = o.getJSONObject("params"); + + assertEquals(0, p.length()); + } + + @Test + public void testGet() throws IOException { + JSONObject o = SimpleHttp.doGet("http://localhost:8081/tojson").param("key-one", "value one ;)").param("key-two", "value two!&").asJson(); + JSONObject p = o.getJSONObject("params"); + + assertEquals(2, p.length()); + assertEquals("value one ;)", p.getString("key-one")); + assertEquals("value two!&", p.getString("key-two")); + } + + @Test + public void testGetCustomHeader() throws IOException { + JSONObject o = SimpleHttp.doGet("http://localhost:8081/tojson").header("Accept", "application/json").header("Authorization", "bearer dsfsadfsdf").asJson(); + JSONObject h = o.getJSONObject("headers"); + + assertEquals("application/json", h.getString("Accept")); + assertEquals("bearer dsfsadfsdf", h.getString("Authorization")); + } + +} diff --git a/social/core/src/main/test/java/org/keycloak/social/utils/ToJsonServlet.java b/social/core/src/main/test/java/org/keycloak/social/utils/ToJsonServlet.java new file mode 100644 index 0000000000..1d738e7da1 --- /dev/null +++ b/social/core/src/main/test/java/org/keycloak/social/utils/ToJsonServlet.java @@ -0,0 +1,50 @@ +package org.keycloak.social.utils; + +import org.json.JSONObject; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Enumeration; + +/** + * @author Stian Thorgersen + */ +public class ToJsonServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + toJson(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + toJson(req, resp); + } + + private void toJson(HttpServletRequest req, HttpServletResponse resp) throws IOException { + JSONObject o = new JSONObject(); + + JSONObject headers = new JSONObject(); + Enumeration headerNames = req.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String n = headerNames.nextElement(); + headers.put(n, req.getHeader(n)); + } + o.put("headers", headers); + + JSONObject params = new JSONObject(); + Enumeration parameterNames = req.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String n = parameterNames.nextElement(); + params.put(n, req.getParameter(n)); + } + o.put("params", params); + + resp.setContentType("application/json"); + resp.getOutputStream().write(o.toString().getBytes()); + } + +} diff --git a/social/core/src/main/test/java/org/keycloak/social/utils/UndertowServer.java b/social/core/src/main/test/java/org/keycloak/social/utils/UndertowServer.java new file mode 100644 index 0000000000..f856a429c6 --- /dev/null +++ b/social/core/src/main/test/java/org/keycloak/social/utils/UndertowServer.java @@ -0,0 +1,63 @@ +package org.keycloak.social.utils; + +import io.undertow.Undertow; +import io.undertow.server.handlers.PathHandler; +import io.undertow.servlet.api.DeploymentInfo; +import io.undertow.servlet.api.DeploymentManager; +import io.undertow.servlet.api.ServletContainer; + +import javax.servlet.ServletException; + +/** + * @author Stian Thorgersen + */ +public class UndertowServer { + + private PathHandler root; + private ServletContainer container; + private Undertow server; + private String hostname; + private int port; + + public UndertowServer(String hostname, int port) { + this.hostname = hostname; + this.port = port; + + root = new PathHandler(); + container = ServletContainer.Factory.newInstance(); + } + + public void start() { + Undertow.Builder builder = Undertow.builder().addListener(port, hostname); + server = builder.setHandler(root).build(); + server.start(); + } + + public void stop() { + if (server != null) { + server.stop(); + server = null; + } + } + + public void deploy(DeploymentInfo deploymentInfo) { + DeploymentManager manager = container.addDeployment(deploymentInfo); + manager.deploy(); + try { + root.addPath(deploymentInfo.getContextPath(), manager.start()); + } catch (ServletException e) { + throw new RuntimeException(e); + } + } + + public void undeploy(String deploymentName) { + DeploymentManager deployment = container.getDeployment(deploymentName); + try { + deployment.stop(); + } catch (ServletException e) { + throw new RuntimeException(e); + } + } + +} + diff --git a/social/google/pom.xml b/social/google/pom.xml index cda69f8847..e7a72d3a23 100755 --- a/social/google/pom.xml +++ b/social/google/pom.xml @@ -17,20 +17,6 @@ org.keycloak keycloak-social-core ${project.version} - provided - - - - com.google.api-client - google-api-client - - - com.google.http-client - google-http-client-jackson - - - com.google.apis - google-api-services-oauth2 diff --git a/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java b/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java index 2f743b796f..87ea93f348 100755 --- a/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java +++ b/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java @@ -21,23 +21,16 @@ */ package org.keycloak.social.google; -import java.util.UUID; - +import org.json.JSONObject; import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthRequest; +import org.keycloak.social.utils.SimpleHttp; import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialUser; -import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; -import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.jackson.JacksonFactory; -import com.google.api.services.oauth2.Oauth2; -import com.google.api.services.oauth2.model.Tokeninfo; -import com.google.api.services.oauth2.model.Userinfo; +import java.util.UUID; /** * @author Stian Thorgersen @@ -48,11 +41,11 @@ public class GoogleProvider implements SocialProvider { private static final String AUTH_PATH = "https://accounts.google.com/o/oauth2/auth"; - private static final String DEFAULT_SCOPE = "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"; + private static final String TOKEN_PATH = "https://accounts.google.com/o/oauth2/token"; - private static final JacksonFactory JSON_FACTORY = new JacksonFactory(); + private static final String PROFILE_PATH = "https://www.googleapis.com/plus/v1/people/me/openIdConnect"; - private static final NetHttpTransport TRANSPORT = new NetHttpTransport(); + private static final String DEFAULT_SCOPE = "openid profile email"; @Override public String getId() { @@ -82,32 +75,22 @@ public class GoogleProvider implements SocialProvider { throw new SocialProviderException("Invalid state"); } - GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(TRANSPORT, JSON_FACTORY, - config.getKey(), config.getSecret(), code, config.getCallbackUrl().toString()) - .execute(); + JSONObject token = SimpleHttp.doPost(TOKEN_PATH).param("code", code).param("client_id", config.getKey()) + .param("client_secret", config.getSecret()) + .param("redirect_uri", config.getCallbackUrl()) + .param("grant_type", "authorization_code").asJson(); - GoogleCredential credential = new GoogleCredential.Builder().setJsonFactory(JSON_FACTORY).setTransport(TRANSPORT) - .setClientSecrets(config.getKey(), config.getSecret()).build() - .setFromTokenResponse(tokenResponse); + String accessToken = token.getString("access_token"); - Oauth2 oauth2 = new Oauth2.Builder(TRANSPORT, JSON_FACTORY, credential).build(); + JSONObject profile = SimpleHttp.doGet(PROFILE_PATH).header("Authorization", "Bearer " + accessToken).asJson(); - Tokeninfo tokenInfo = oauth2.tokeninfo().setAccessToken(credential.getAccessToken()).execute(); + SocialUser user = new SocialUser(profile.getString("sub")); - if (tokenInfo.containsKey("error")) { - throw new SocialProviderException((String) tokenInfo.get("error")); - } + user.setUsername(profile.getString("email")); - Userinfo userInfo = oauth2.userinfo().get().execute(); - - SocialUser user = new SocialUser(userInfo.getId()); - - // Use email as username for Google - user.setUsername(userInfo.getEmail()); - - user.setFirstName(userInfo.getGivenName()); - user.setLastName(userInfo.getFamilyName()); - user.setEmail(userInfo.getEmail()); + user.setFirstName(profile.optString("given_name")); + user.setLastName(profile.optString("family_name")); + user.setEmail(profile.optString("email")); return user; } catch (Exception e) { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java index bab9f434fa..5e5bd1dc3e 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java @@ -42,7 +42,7 @@ public class DummySocial implements SocialProvider { throw new SocialProviderException("Invalid state"); } - String username = callback.getQueryParam("access_token"); + String username = callback.getQueryParam("username"); SocialUser user = new SocialUser(username); user.setEmail(username + "@dummy-social"); user.setUsername(username); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java index f6a49112e4..fe55e34a2c 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java @@ -12,6 +12,7 @@ import java.io.PrintWriter; import java.net.URI; import java.nio.charset.Charset; import java.util.List; +import java.util.UUID; public class DummySocialServlet extends HttpServlet { @@ -31,7 +32,6 @@ public class DummySocialServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String accessToken = req.getParameter("username"); String state = null; String redirectUri = null; @@ -50,7 +50,7 @@ public class DummySocialServlet extends HttpServlet { } } - String redirect = redirectUri + "?access_token=" + accessToken + "&token_type=bearer&state=" + state; + String redirect = redirectUri + "?username=" + req.getParameter("username") + "&state=" + state + "&code=" + UUID.randomUUID().toString(); resp.sendRedirect(redirect); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java index d25959d6e1..9fe02ef3fb 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java @@ -59,6 +59,18 @@ public class LoginUpdateProfilePage extends AbstractPage { return loginErrorMessage != null ? loginErrorMessage.getText() : null; } + public String getFirstName() { + return firstNameInput.getAttribute("value"); + } + + public String getLastName() { + return lastNameInput.getAttribute("value"); + } + + public String getEmail() { + return emailInput.getAttribute("value"); + } + public boolean isCurrent() { return driver.getTitle().equals("Update Account Information"); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java index d247d392df..d1b3d51b95 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java @@ -35,6 +35,7 @@ import org.keycloak.testsuite.OAuthClient.AccessTokenResponse; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.LoginUpdateProfilePage; import org.keycloak.testsuite.pages.RegisterPage; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; @@ -55,7 +56,7 @@ public class SocialLoginTest { @Override public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) { appRealm.setSocial(true); - appRealm.setAutomaticRegistrationAfterSocialLogin(true); + appRealm.setUpdateProfileOnInitialSocialLogin(false); HashMap socialConfig = new HashMap(); socialConfig.put("dummy.key", "1234"); @@ -77,7 +78,7 @@ public class SocialLoginTest { protected LoginPage loginPage; @WebResource - protected RegisterPage registerPage; + protected LoginUpdateProfilePage profilePage; @WebResource protected OAuthClient oauth; @@ -106,11 +107,11 @@ public class SocialLoginTest { } @Test - public void registerRequired() { + public void profileUpdateRequired() { keycloakRule.configure(new KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { - appRealm.setAutomaticRegistrationAfterSocialLogin(false); + appRealm.setUpdateProfileOnInitialSocialLogin(true); } }); @@ -122,21 +123,20 @@ public class SocialLoginTest { driver.findElement(By.id("username")).sendKeys("dummy-user-reg"); driver.findElement(By.id("submit")).click(); - registerPage.isCurrent(); + profilePage.isCurrent(); - Assert.assertEquals("", registerPage.getFirstName()); - Assert.assertEquals("", registerPage.getLastName()); - Assert.assertEquals("dummy-user-reg@dummy-social", registerPage.getEmail()); - Assert.assertEquals("dummy-user-reg", registerPage.getUsername()); + Assert.assertEquals("", profilePage.getFirstName()); + Assert.assertEquals("", profilePage.getLastName()); + Assert.assertEquals("dummy-user-reg@dummy-social", profilePage.getEmail()); - registerPage.register("Dummy", "User", "dummy-user-reg@dummy-social", "dummy-user-reg", "password", "password"); + profilePage.update("Dummy", "User", "dummy-user-reg@dummy-social"); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); } finally { keycloakRule.configure(new KeycloakSetup() { @Override public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { - appRealm.setAutomaticRegistrationAfterSocialLogin(true); + appRealm.setUpdateProfileOnInitialSocialLogin(false); } }); }