Added SocialLink, which represents binding between User and his social username for particular socialProvider
This commit is contained in:
parent
5d33697596
commit
1a374a8d1b
11 changed files with 287 additions and 5 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue