Added SocialLink, which represents binding between User and his social username for particular socialProvider

This commit is contained in:
mposolda 2013-08-19 14:36:06 +02:00
parent 5d33697596
commit 1a374a8d1b
11 changed files with 287 additions and 5 deletions

View file

@ -29,6 +29,7 @@ public class RealmRepresentation {
protected List<UserRepresentation> users; protected List<UserRepresentation> users;
protected List<RoleMappingRepresentation> roleMappings; protected List<RoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings; protected List<ScopeMappingRepresentation> scopeMappings;
protected List<SocialMappingRepresentation> socialMappings;
protected List<ApplicationRepresentation> applications; protected List<ApplicationRepresentation> applications;
@ -144,6 +145,18 @@ public class RealmRepresentation {
return mapping; 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() { public Set<String> getRequiredCredentials() {
return requiredCredentials; return requiredCredentials;
} }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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) { public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {

View file

@ -127,6 +127,14 @@ public interface RealmModel {
void updateRequiredApplicationCredentials(Set<String> creds); 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(); boolean isSocial();
void setSocial(boolean social); void setSocial(boolean social);

View file

@ -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;
}
}

View file

@ -10,6 +10,7 @@ import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel; import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ApplicationModel; import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.SocialLinkModel;
import org.keycloak.services.models.UserCredentialModel; import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel; import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.picketlink.mappings.RealmData; 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.UsernamePasswordCredentials;
import org.picketlink.idm.credential.X509CertificateCredentials; import org.picketlink.idm.credential.X509CertificateCredentials;
import org.picketlink.idm.model.IdentityType; 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.Grant;
import org.picketlink.idm.model.sample.Role; import org.picketlink.idm.model.sample.Role;
import org.picketlink.idm.model.sample.SampleModel; import org.picketlink.idm.model.sample.SampleModel;
@ -694,4 +694,54 @@ public class RealmAdapter implements RealmModel {
realm.setDefaultRoles(defaultRoles); realm.setDefaultRoles(defaultRoles);
updateRealm(); 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);
}
} }

View file

@ -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));
}
}

View file

@ -109,10 +109,6 @@ public class SocialResource {
return oauth.forwardToSecurityFailure("Realm not enabled."); return oauth.forwardToSecurityFailure("Realm not enabled.");
} }
if (!realm.isEnabled()) {
return oauth.forwardToSecurityFailure("Realm not enabled.");
}
String clientId = requestData.getClientAttributes().get("clientId"); String clientId = requestData.getClientAttributes().get("clientId");
UserModel client = realm.getUser(clientId); UserModel client = realm.getUser(clientId);

View file

@ -15,6 +15,7 @@ import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel; import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ApplicationModel; import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.SocialLinkModel;
import org.keycloak.services.models.UserModel; import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.SaasService; import org.keycloak.services.resources.SaasService;
@ -82,6 +83,8 @@ public class ImportTest {
Set<String> scopes = realm.getScope(user); Set<String> scopes = realm.getScope(user);
System.out.println("Scopes size: " + scopes.size()); System.out.println("Scopes size: " + scopes.size());
Assert.assertTrue(scopes.contains("*")); Assert.assertTrue(scopes.contains("*"));
Assert.assertEquals(0, realm.getSocialLinks(user).size());
List<ApplicationModel> resources = realm.getApplications(); List<ApplicationModel> resources = realm.getApplications();
Assert.assertEquals(2, resources.size()); Assert.assertEquals(2, resources.size());
List<RealmModel> realms = identitySession.getRealms(admin); List<RealmModel> realms = identitySession.getRealms(admin);
@ -94,6 +97,28 @@ public class ImportTest {
Assert.assertNotNull(oauthClient); Assert.assertNotNull(oauthClient);
Set<String> appScopes = application.getScope(oauthClient); Set<String> appScopes = application.getScope(oauthClient);
Assert.assertTrue(appScopes.contains("user")); 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 @Test

View file

@ -50,6 +50,10 @@
"value": "clientpassword" "value": "clientpassword"
} }
] ]
},
{
"username": "mySocialUser",
"enabled": true
} }
], ],
"roleMappings": [ "roleMappings": [
@ -64,6 +68,25 @@
"roles": ["*"] "roles": ["*"]
} }
], ],
"socialMappings": [
{
"username": "mySocialUser",
"socialLinks": [
{
"socialProvider": "facebook",
"socialUsername": "fbuser1"
},
{
"socialProvider": "facebook",
"socialUsername": "fbuser2"
},
{
"socialProvider": "google",
"socialUsername": "mySocialUser@gmail.com"
}
]
}
],
"applications": [ "applications": [
{ {
"name": "Application", "name": "Application",