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<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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.");
|
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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue