diff --git a/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java b/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
index 2fc9a29107..a3e60d2460 100644
--- a/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
@@ -5,6 +5,6 @@ package org.keycloak.account;
*/
public enum AccountPages {
- ACCOUNT, PASSWORD, TOTP;
+ ACCOUNT, PASSWORD, TOTP, SOCIAL;
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
index 64f41354ad..1a5cc6720b 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
@@ -4,6 +4,7 @@ import org.jboss.resteasy.logging.Logger;
import org.keycloak.account.Account;
import org.keycloak.account.AccountPages;
import org.keycloak.account.freemarker.model.AccountBean;
+import org.keycloak.account.freemarker.model.AccountSocialBean;
import org.keycloak.account.freemarker.model.MessageBean;
import org.keycloak.account.freemarker.model.ReferrerBean;
import org.keycloak.account.freemarker.model.TotpBean;
@@ -88,6 +89,10 @@ public class FreeMarkerAccount implements Account {
attributes.put("url", new UrlBean(realm, theme, baseUri));
+ if (realm.isSocial()) {
+ attributes.put("isSocialRealm", true);
+ }
+
switch (page) {
case ACCOUNT:
attributes.put("account", new AccountBean(user));
@@ -95,6 +100,9 @@ public class FreeMarkerAccount implements Account {
case TOTP:
attributes.put("totp", new TotpBean(user, baseUri));
break;
+ case SOCIAL:
+ attributes.put("social", new AccountSocialBean(realm, user, uriInfo.getBaseUri()));
+ break;
}
try {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
index 4e701a3e31..5a63ef5949 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
@@ -15,6 +15,8 @@ public class Templates {
return "password.ftl";
case TOTP:
return "totp.ftl";
+ case SOCIAL:
+ return "social.ftl";
default:
throw new IllegalArgumentException();
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
new file mode 100644
index 0000000000..ed94d6980b
--- /dev/null
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java
@@ -0,0 +1,95 @@
+package org.keycloak.account.freemarker.model;
+
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.social.SocialLoader;
+import org.keycloak.social.SocialProvider;
+
+/**
+ * @author Marek Posolda
+ */
+public class AccountSocialBean {
+
+ private final List socialLinks;
+
+ public AccountSocialBean(RealmModel realm, UserModel user, URI baseUri) {
+ URI accountSocialUpdateUri = Urls.accountSocialUpdate(baseUri, realm.getName());
+ this.socialLinks = new LinkedList();
+
+ Map socialConfig = realm.getSocialConfig();
+ Set userSocialLinks = realm.getSocialLinks(user);
+
+ if (socialConfig != null && !socialConfig.isEmpty()) {
+ for (SocialProvider provider : SocialLoader.load()) {
+ String socialProviderId = provider.getId();
+ if (socialConfig.containsKey(socialProviderId + ".key")) {
+ String socialUsername = getSocialUsername(userSocialLinks, socialProviderId);
+
+ String action = socialUsername!=null ? "remove" : "add";
+ String actionUrl = UriBuilder.fromUri(accountSocialUpdateUri).queryParam("action", action).queryParam("provider_id", socialProviderId).build().toString();
+
+ SocialLinkEntry entry = new SocialLinkEntry(socialProviderId, provider.getName(), socialUsername, actionUrl);
+ this.socialLinks.add(entry);
+ }
+ }
+ }
+ }
+
+ private String getSocialUsername(Set userSocialLinks, String socialProviderId) {
+ for (SocialLinkModel link : userSocialLinks) {
+ if (socialProviderId.equals(link.getSocialProvider())) {
+ return link.getSocialUsername();
+ }
+ }
+ return null;
+ }
+
+ public List getLinks() {
+ return socialLinks;
+ }
+
+ public class SocialLinkEntry {
+
+ private final String providerId;
+ private final String providerName;
+ private final String socialUsername;
+ private final String actionUrl;
+
+ public SocialLinkEntry(String providerId, String providerName, String socialUsername, String actionUrl) {
+ this.providerId = providerId;
+ this.providerName = providerName;
+ this.socialUsername = socialUsername!=null ? socialUsername : "";
+ this.actionUrl = actionUrl;
+ }
+
+ public String getProviderId() {
+ return providerId;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public String getSocialUsername() {
+ return socialUsername;
+ }
+
+ public boolean isConnected() {
+ return !socialUsername.isEmpty();
+ }
+
+ public String getActionUrl() {
+ return actionUrl;
+ }
+ }
+}
diff --git a/forms/common-themes/src/main/resources/theme/account/base/draft.social.ftl b/forms/common-themes/src/main/resources/theme/account/base/draft.social.ftl
deleted file mode 100755
index b2b3358abd..0000000000
--- a/forms/common-themes/src/main/resources/theme/account/base/draft.social.ftl
+++ /dev/null
@@ -1,38 +0,0 @@
-<#-- TODO: Only a placeholder, implementation needed -->
-<#import "template.ftl" as layout>
-<@layout.mainLayout active='social' bodyClass='social'; section>
-
- <#if section = "header">
-
-
Social Accounts
-
- <#elseif section = "content">
-
-
-
- #if>
-@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
index 831a189056..b68a25ebfc 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
@@ -24,4 +24,13 @@ successTotp=Google authenticator configured.
successTotpRemoved=Google authenticator removed.
accountUpdated=Your account has been updated
-accountPasswordUpdated=Your password has been updated
\ No newline at end of file
+accountPasswordUpdated=Your password has been updated
+
+missingSocialProvider=Social provider not specified
+invalidSocialAction=Invalid or missing action
+socialProviderNotFound=Specified social provider not found
+socialLinkNotActive=This social link is not active anymore
+socialRedirectError=Failed to redirect to social provider
+socialProviderRemoved=Social provider removed successfully
+
+accountDisabled=Account is disabled, contact admin
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/account/base/social.ftl b/forms/common-themes/src/main/resources/theme/account/base/social.ftl
new file mode 100644
index 0000000000..a1941de571
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/account/base/social.ftl
@@ -0,0 +1,30 @@
+<#import "template.ftl" as layout>
+<@layout.mainLayout active='social' bodyClass='social'; section>
+
+
+
+
Social Accounts
+
+
+
+
+
+@layout.mainLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
index 1ff34c4246..56941ff03b 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
@@ -28,7 +28,7 @@