keycloak-memoizeit

Added SocialLink, which represents binding between User and

8/19/2013 9:36:06 AM

Details

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 e365a61..525ed74 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -29,6 +29,7 @@ public class RealmRepresentation {
     protected List<UserRepresentation> users;
     protected List<RoleMappingRepresentation> roleMappings;
     protected List<ScopeMappingRepresentation> scopeMappings;
+    protected List<SocialMappingRepresentation> socialMappings;
     protected List<ApplicationRepresentation> applications;
 
 
@@ -144,6 +145,18 @@ public class RealmRepresentation {
         return mapping;
     }
 
+    public List<SocialMappingRepresentation> getSocialMappings() {
+        return socialMappings;
+    }
+
+    public SocialMappingRepresentation socialMapping(String username) {
+        SocialMappingRepresentation mapping = new SocialMappingRepresentation();
+        mapping.setUsername(username);
+        if (socialMappings == null) socialMappings = new ArrayList<SocialMappingRepresentation>();
+        socialMappings.add(mapping);
+        return mapping;
+    }
+
     public Set<String> getRequiredCredentials() {
         return requiredCredentials;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java
new file mode 100644
index 0000000..a6e1838
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java
@@ -0,0 +1,26 @@
+package org.keycloak.representations.idm;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SocialLinkRepresentation {
+
+    protected String socialProvider;
+    protected String socialUsername;
+
+    public String getSocialProvider() {
+        return socialProvider;
+    }
+
+    public void setSocialProvider(String socialProvider) {
+        this.socialProvider = socialProvider;
+    }
+
+    public String getSocialUsername() {
+        return socialUsername;
+    }
+
+    public void setSocialUsername(String socialUsername) {
+        this.socialUsername = socialUsername;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java
new file mode 100644
index 0000000..57dd874
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java
@@ -0,0 +1,43 @@
+package org.keycloak.representations.idm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SocialMappingRepresentation {
+
+    protected String self; // link
+    protected String username;
+    protected List<SocialLinkRepresentation> socialLinks;
+
+    public String getSelf() {
+        return self;
+    }
+
+    public void setSelf(String self) {
+        this.self = self;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public List<SocialLinkRepresentation> getSocialLinks() {
+        return socialLinks;
+    }
+
+    public SocialLinkRepresentation socialLink(String socialProvider, String socialUsername) {
+        SocialLinkRepresentation link = new SocialLinkRepresentation();
+        link.setSocialProvider(socialProvider);
+        link.setSocialUsername(socialUsername);
+        if (socialLinks == null) socialLinks = new ArrayList<SocialLinkRepresentation>();
+        socialLinks.add(link);
+        return link;
+    }
+}
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 afd2406..506503e 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -182,6 +182,16 @@ public class RealmManager {
 
             }
         }
+
+        if (rep.getSocialMappings() != null) {
+            for (SocialMappingRepresentation socialMapping : rep.getSocialMappings()) {
+                UserModel user = userMap.get(socialMapping.getUsername());
+                for (SocialLinkRepresentation link : socialMapping.getSocialLinks()) {
+                    SocialLinkModel mappingModel = new SocialLinkModel(link.getSocialProvider(), link.getSocialUsername());
+                    newRealm.addSocialLink(user, mappingModel);
+                }
+            }
+        }
     }
 
     public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
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 dc8d6da..816aab4 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
@@ -10,6 +10,7 @@ import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
 import org.keycloak.services.models.ApplicationModel;
 import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.SocialLinkModel;
 import org.keycloak.services.models.UserCredentialModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.models.picketlink.mappings.RealmData;
@@ -26,7 +27,6 @@ import org.picketlink.idm.credential.TOTPCredentials;
 import org.picketlink.idm.credential.UsernamePasswordCredentials;
 import org.picketlink.idm.credential.X509CertificateCredentials;
 import org.picketlink.idm.model.IdentityType;
-import org.picketlink.idm.model.annotation.AttributeProperty;
 import org.picketlink.idm.model.sample.Grant;
 import org.picketlink.idm.model.sample.Role;
 import org.picketlink.idm.model.sample.SampleModel;
@@ -694,4 +694,54 @@ public class RealmAdapter implements RealmModel {
         realm.setDefaultRoles(defaultRoles);
         updateRealm();
     }
+
+    @Override
+    public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
+        RelationshipQuery<SocialLinkRelationship> query = getRelationshipManager().createRelationshipQuery(SocialLinkRelationship.class);
+        query.setParameter(SocialLinkRelationship.SOCIAL_PROVIDER, socialLink.getSocialProvider());
+        query.setParameter(SocialLinkRelationship.SOCIAL_USERNAME, socialLink.getSocialUsername());
+        List<SocialLinkRelationship> results = query.getResultList();
+        if (results.isEmpty()) {
+            return null;
+        } else if (results.size() > 1) {
+            throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() +
+                    ", socialUsername=" + socialLink.getSocialUsername() + ", results=" + results);
+        } else {
+            User user = results.get(0).getUser();
+            return new UserAdapter(user, getIdm());
+        }
+    }
+
+    @Override
+    public Set<SocialLinkModel> getSocialLinks(UserModel user) {
+        RelationshipQuery<SocialLinkRelationship> query = getRelationshipManager().createRelationshipQuery(SocialLinkRelationship.class);
+        query.setParameter(SocialLinkRelationship.USER, ((UserAdapter)user).getUser());
+        List<SocialLinkRelationship> plSocialLinks = query.getResultList();
+
+        Set<SocialLinkModel> results = new HashSet<SocialLinkModel>();
+        for (SocialLinkRelationship relationship : plSocialLinks) {
+            results.add(new SocialLinkModel(relationship.getSocialProvider(), relationship.getSocialUsername()));
+        }
+        return results;
+    }
+
+    @Override
+    public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
+        SocialLinkRelationship relationship = new SocialLinkRelationship();
+        relationship.setUser(((UserAdapter)user).getUser());
+        relationship.setSocialProvider(socialLink.getSocialProvider());
+        relationship.setSocialUsername(socialLink.getSocialUsername());
+
+        getRelationshipManager().add(relationship);
+    }
+
+    @Override
+    public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
+        SocialLinkRelationship relationship = new SocialLinkRelationship();
+        relationship.setUser(((UserAdapter)user).getUser());
+        relationship.setSocialProvider(socialLink.getSocialProvider());
+        relationship.setSocialUsername(socialLink.getSocialUsername());
+
+        getRelationshipManager().remove(relationship);
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/relationships/SocialLinkRelationship.java b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/SocialLinkRelationship.java
new file mode 100644
index 0000000..c3a9389
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/SocialLinkRelationship.java
@@ -0,0 +1,57 @@
+package org.keycloak.services.models.picketlink.relationships;
+
+import org.picketlink.idm.model.AbstractAttributedType;
+import org.picketlink.idm.model.Attribute;
+import org.picketlink.idm.model.Relationship;
+import org.picketlink.idm.model.sample.User;
+import org.picketlink.idm.query.AttributeParameter;
+import org.picketlink.idm.query.RelationshipQueryParameter;
+
+/**
+ * Binding between user and his social username for particular Social provider
+ *
+ * Example: Keycloak user "john" has username "john123" in social provider "facebook"
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SocialLinkRelationship extends AbstractAttributedType implements Relationship {
+
+    private static final long serialVersionUID = 154879L;
+
+    public static final AttributeParameter SOCIAL_PROVIDER = new AttributeParameter("socialProvider");
+    public static final AttributeParameter SOCIAL_USERNAME = new AttributeParameter("socialUsername");
+
+    public static final RelationshipQueryParameter USER = new RelationshipQueryParameter() {
+
+        @Override
+        public String getName() {
+            return "user";
+        }
+    };
+
+    private User user;
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public String getSocialProvider() {
+        return (String)getAttribute("socialProvider").getValue();
+    }
+
+    public void setSocialProvider(String socialProvider) {
+        setAttribute(new Attribute<String>("socialProvider", socialProvider));
+    }
+
+    public String getSocialUsername() {
+        return (String)getAttribute("socialUsername").getValue();
+    }
+
+    public void setSocialUsername(String socialProviderUserId) {
+        setAttribute(new Attribute<String>("socialUsername", socialProviderUserId));
+    }
+}
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 408df1b..8ccc942 100755
--- a/services/src/main/java/org/keycloak/services/models/RealmModel.java
+++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java
@@ -127,6 +127,14 @@ public interface RealmModel {
 
     void updateRequiredApplicationCredentials(Set<String> creds);
 
+    UserModel getUserBySocialLink(SocialLinkModel socialLink);
+
+    Set<SocialLinkModel> getSocialLinks(UserModel user);
+
+    void addSocialLink(UserModel user, SocialLinkModel socialLink);
+
+    void removeSocialLink(UserModel user, SocialLinkModel socialLink);
+
     boolean isSocial();
 
     void setSocial(boolean social);
diff --git a/services/src/main/java/org/keycloak/services/models/SocialLinkModel.java b/services/src/main/java/org/keycloak/services/models/SocialLinkModel.java
new file mode 100644
index 0000000..7a92d8d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/SocialLinkModel.java
@@ -0,0 +1,31 @@
+package org.keycloak.services.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SocialLinkModel {
+
+    private String socialUsername;
+    private String socialProvider;
+
+    public SocialLinkModel(String socialProvider, String socialUsername) {
+        this.socialUsername = socialUsername;
+        this.socialProvider = socialProvider;
+    }
+
+    public String getSocialUsername() {
+        return socialUsername;
+    }
+
+    public void setSocialUsername(String socialUsername) {
+        this.socialUsername = socialUsername;
+    }
+
+    public String getSocialProvider() {
+        return socialProvider;
+    }
+
+    public void setSocialProvider(String socialProvider) {
+        this.socialProvider = socialProvider;
+    }
+}
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 73e139c..91a36ca 100644
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -109,10 +109,6 @@ public class SocialResource {
                     return oauth.forwardToSecurityFailure("Realm not enabled.");
                 }
 
-                if (!realm.isEnabled()) {
-                    return oauth.forwardToSecurityFailure("Realm not enabled.");
-                }
-
                 String clientId = requestData.getClientAttributes().get("clientId");
 
                 UserModel client = realm.getUser(clientId);
diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java
index 68cd9ed..95590f7 100755
--- a/services/src/test/java/org/keycloak/test/ImportTest.java
+++ b/services/src/test/java/org/keycloak/test/ImportTest.java
@@ -15,6 +15,7 @@ import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
 import org.keycloak.services.models.ApplicationModel;
 import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.SocialLinkModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.resources.KeycloakApplication;
 import org.keycloak.services.resources.SaasService;
@@ -82,6 +83,8 @@ public class ImportTest {
         Set<String> scopes = realm.getScope(user);
         System.out.println("Scopes size: " + scopes.size());
         Assert.assertTrue(scopes.contains("*"));
+        Assert.assertEquals(0, realm.getSocialLinks(user).size());
+
         List<ApplicationModel> resources = realm.getApplications();
         Assert.assertEquals(2, resources.size());
         List<RealmModel> realms = identitySession.getRealms(admin);
@@ -94,6 +97,28 @@ public class ImportTest {
         Assert.assertNotNull(oauthClient);
         Set<String> appScopes = application.getScope(oauthClient);
         Assert.assertTrue(appScopes.contains("user"));
+
+        // Test social linking
+        UserModel socialUser = realm.getUser("mySocialUser");
+        Set<SocialLinkModel> socialLinks = realm.getSocialLinks(socialUser);
+        Assert.assertEquals(3, socialLinks.size());
+        int facebookCount = 0;
+        int googleCount = 0;
+        for (SocialLinkModel socialLinkModel : socialLinks) {
+            if ("facebook".equals(socialLinkModel.getSocialProvider())) {
+                facebookCount++;
+            } else if ("google".equals(socialLinkModel.getSocialProvider())) {
+                googleCount++;
+                Assert.assertEquals(socialLinkModel.getSocialUsername(), "mySocialUser@gmail.com");
+            }
+        }
+        Assert.assertEquals(2, facebookCount);
+        Assert.assertEquals(1, googleCount);
+
+        UserModel foundSocialUser = realm.getUserBySocialLink(new SocialLinkModel("facebook", "fbuser1"));
+        Assert.assertEquals(foundSocialUser.getLoginName(), socialUser.getLoginName());
+        Assert.assertNull(realm.getUserBySocialLink(new SocialLinkModel("facebook", "not-existing")));
+
     }
 
     @Test
diff --git a/services/src/test/resources/testrealm.json b/services/src/test/resources/testrealm.json
index b1e3e39..b2c4454 100755
--- a/services/src/test/resources/testrealm.json
+++ b/services/src/test/resources/testrealm.json
@@ -50,6 +50,10 @@
                     "value": "clientpassword"
                 }
             ]
+        },
+        {
+            "username": "mySocialUser",
+            "enabled": true
         }
     ],
     "roleMappings": [
@@ -64,6 +68,25 @@
             "roles": ["*"]
         }
     ],
+    "socialMappings": [
+        {
+            "username": "mySocialUser",
+            "socialLinks": [
+                {
+                    "socialProvider": "facebook",
+                    "socialUsername": "fbuser1"
+                },
+                {
+                    "socialProvider": "facebook",
+                    "socialUsername": "fbuser2"
+                },
+                {
+                    "socialProvider": "google",
+                    "socialUsername": "mySocialUser@gmail.com"
+                }
+            ]
+        }
+    ],
     "applications": [
         {
             "name": "Application",