federation iteration
This commit is contained in:
parent
b999c1e5b1
commit
148d494905
49 changed files with 2133 additions and 143 deletions
|
@ -4,7 +4,7 @@
|
|||
<artifactId>keycloak-authentication-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.FederationProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package org.keycloak.representations;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class FederationProviderRepresentation {
|
||||
|
||||
private String id;
|
||||
private String providerName;
|
||||
private Map<String, String> config;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FederationProviderRepresentation that = (FederationProviderRepresentation) o;
|
||||
|
||||
if (!id.equals(that.id)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.representations.idm;
|
||||
|
||||
import org.keycloak.representations.FederationProviderRepresentation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -56,6 +58,7 @@ public class RealmRepresentation {
|
|||
protected Map<String, String> smtpServer;
|
||||
protected Map<String, String> ldapServer;
|
||||
protected List<AuthenticationProviderRepresentation> authenticationProviders;
|
||||
protected List<FederationProviderRepresentation> federationProviders;
|
||||
protected String loginTheme;
|
||||
protected String accountTheme;
|
||||
protected String adminTheme;
|
||||
|
@ -467,4 +470,12 @@ public class RealmRepresentation {
|
|||
public void setAuditListeners(List<String> auditListeners) {
|
||||
this.auditListeners = auditListeners;
|
||||
}
|
||||
|
||||
public List<FederationProviderRepresentation> getFederationProviders() {
|
||||
return federationProviders;
|
||||
}
|
||||
|
||||
public void setFederationProviders(List<FederationProviderRepresentation> federationProviders) {
|
||||
this.federationProviders = federationProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public class UserRepresentation {
|
|||
protected String lastName;
|
||||
protected String email;
|
||||
protected AuthenticationLinkRepresentation authenticationLink;
|
||||
protected String federationLink;
|
||||
protected Map<String, String> attributes;
|
||||
protected List<CredentialRepresentation> credentials;
|
||||
protected List<String> requiredActions;
|
||||
|
@ -170,4 +171,12 @@ public class UserRepresentation {
|
|||
public void setApplicationRoles(Map<String, List<String>> applicationRoles) {
|
||||
this.applicationRoles = applicationRoles;
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -308,6 +308,7 @@ public class ExportUtils {
|
|||
credReps.add(credRep);
|
||||
}
|
||||
userRep.setCredentials(credReps);
|
||||
userRep.setFederationLink(user.getFederationLink());
|
||||
|
||||
return userRep;
|
||||
}
|
||||
|
|
88
federation/ldap/pom.xml
Executable file
88
federation/ldap/pom.xml
Executable file
|
@ -0,0 +1,88 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-authentication-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-ldap-federation</artifactId>
|
||||
<name>Keycloak LDAP Federation</name>
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-idm-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-idm-impl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-idm-simple-schema</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,89 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.keycloak.models.utils.reflection.Reflections;
|
||||
import org.picketlink.idm.config.LDAPMappingConfiguration;
|
||||
import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
|
||||
import org.picketlink.idm.ldap.internal.LDAPOperationManager;
|
||||
import org.picketlink.idm.model.AttributedType;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
import org.picketlink.idm.query.IdentityQuery;
|
||||
import org.picketlink.idm.spi.IdentityContext;
|
||||
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.picketlink.common.constants.LDAPConstants.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class KeycloakLDAPIdentityStore extends LDAPIdentityStore {
|
||||
|
||||
public static Method GET_OPERATION_MANAGER_METHOD;
|
||||
public static Method CREATE_SEARCH_FILTER_METHOD;
|
||||
public static Method EXTRACT_ATTRIBUTES_METHOD;
|
||||
public static Method GET_ENTRY_IDENTIFIER_METHOD;
|
||||
|
||||
public static final String SAM_ACCOUNT_NAME = "sAMAccountName";
|
||||
|
||||
static {
|
||||
GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
|
||||
CREATE_SEARCH_FILTER_METHOD = getMethodOnLDAPStore("createIdentityTypeSearchFilter", IdentityQuery.class, LDAPMappingConfiguration.class);
|
||||
EXTRACT_ATTRIBUTES_METHOD = getMethodOnLDAPStore("extractAttributes", AttributedType.class, boolean.class);
|
||||
GET_ENTRY_IDENTIFIER_METHOD = getMethodOnLDAPStore("getEntryIdentifier", AttributedType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttributedType(IdentityContext context, AttributedType attributedType) {
|
||||
LDAPMappingConfiguration userMappingConfig = getConfig().getMappingConfig(attributedType.getClass());
|
||||
String ldapUsernameAttrName = userMappingConfig.getMappedProperties().get(userMappingConfig.getIdProperty().getName());
|
||||
|
||||
if (getConfig().isActiveDirectory() && SAM_ACCOUNT_NAME.equals(ldapUsernameAttrName)) {
|
||||
// TODO: pain, but everything in picketlink is private... Improve if possible...
|
||||
LDAPOperationManager operationManager = Reflections.invokeMethod(false, GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, this);
|
||||
|
||||
String cn = getCommonName(attributedType);
|
||||
String bindingDN = CN + EQUAL + cn + COMMA + userMappingConfig.getBaseDN();
|
||||
|
||||
BasicAttributes ldapAttributes = Reflections.invokeMethod(false, EXTRACT_ATTRIBUTES_METHOD, BasicAttributes.class, this, attributedType, true);
|
||||
ldapAttributes.put(CN, cn);
|
||||
|
||||
operationManager.createSubContext(bindingDN, ldapAttributes);
|
||||
|
||||
String ldapId = Reflections.invokeMethod(false, GET_ENTRY_IDENTIFIER_METHOD, String.class, this, attributedType);
|
||||
attributedType.setId(ldapId);
|
||||
} else {
|
||||
super.addAttributedType(context, attributedType);
|
||||
}
|
||||
}
|
||||
|
||||
// Hack as methods are protected on LDAPIdentityStore :/
|
||||
public static Method getMethodOnLDAPStore(String methodName, Class... classes) {
|
||||
try {
|
||||
Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getCommonName(AttributedType aType) {
|
||||
User user = (User)aType;
|
||||
String fullName;
|
||||
if (user.getFirstName() != null && user.getLastName() != null) {
|
||||
fullName = user.getFirstName() + " " + user.getLastName();
|
||||
} else if (user.getFirstName() != null && user.getFirstName().trim().length() > 0) {
|
||||
fullName = user.getFirstName();
|
||||
} else {
|
||||
fullName = user.getLastName();
|
||||
}
|
||||
|
||||
// Fallback to loginName
|
||||
if (fullName == null || fullName.trim().length() == 0) {
|
||||
fullName = user.getLoginName();
|
||||
}
|
||||
|
||||
return fullName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.keycloak.models.FederationProvider;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.picketlink.idm.IdentityManagementException;
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.PartitionManager;
|
||||
import org.picketlink.idm.credential.Credentials;
|
||||
import org.picketlink.idm.credential.Password;
|
||||
import org.picketlink.idm.credential.UsernamePasswordCredentials;
|
||||
import org.picketlink.idm.model.basic.BasicModel;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
|
||||
import static org.picketlink.idm.IDMMessages.MESSAGES;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LDAPFederationProvider implements FederationProvider {
|
||||
protected KeycloakSession session;
|
||||
protected FederationProviderModel model;
|
||||
protected PartitionManager partitionManager;
|
||||
|
||||
protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
|
||||
|
||||
static
|
||||
{
|
||||
supportedCredentialTypes.add(UserCredentialModel.PASSWORD);
|
||||
}
|
||||
|
||||
public LDAPFederationProvider(KeycloakSession session, FederationProviderModel model, PartitionManager partitionManager) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.partitionManager = partitionManager;
|
||||
}
|
||||
|
||||
private ModelException convertIDMException(IdentityManagementException ie) {
|
||||
Throwable realCause = ie;
|
||||
while (realCause.getCause() != null) {
|
||||
realCause = realCause.getCause();
|
||||
}
|
||||
|
||||
// Use the message from the realCause
|
||||
return new ModelException(realCause.getMessage(), ie);
|
||||
}
|
||||
|
||||
public KeycloakSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public FederationProviderModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public PartitionManager getPartitionManager() {
|
||||
return partitionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel proxy(UserModel local) {
|
||||
// todo
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedCredentialTypes() {
|
||||
return supportedCredentialTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, UserModel user) {
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = new User(user.getUsername());
|
||||
picketlinkUser.setFirstName(user.getFirstName());
|
||||
picketlinkUser.setLastName(user.getLastName());
|
||||
picketlinkUser.setEmail(user.getEmail());
|
||||
identityManager.add(picketlinkUser);
|
||||
return proxy(user);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, UserModel user) {
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = BasicModel.getUser(identityManager, user.getUsername());
|
||||
if (picketlinkUser == null) {
|
||||
return false;
|
||||
}
|
||||
identityManager.remove(picketlinkUser);
|
||||
return true;
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdminPage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getAdminClass() {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
|
||||
throw new IllegalAccessError("Not allowed to call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
throw new IllegalAccessError("Not allowed to call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, String name) {
|
||||
throw new IllegalAccessError("Not allowed to call this");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
|
||||
session.userStorage().addSocialLink(realm, user, socialLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
|
||||
return session.userStorage().removeSocialLink(realm, user, socialProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = BasicModel.getUser(identityManager, username);
|
||||
if (picketlinkUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return importUserFromPicketlink(realm, picketlinkUser);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityManager getIdentityManager() {
|
||||
return partitionManager.createIdentityManager();
|
||||
}
|
||||
|
||||
protected UserModel importUserFromPicketlink(RealmModel realm, User picketlinkUser) {
|
||||
String email = (picketlinkUser.getEmail() != null && picketlinkUser.getEmail().trim().length() > 0) ? picketlinkUser.getEmail() : null;
|
||||
UserModel imported = session.userStorage().addUser(realm, picketlinkUser.getLoginName());
|
||||
imported.setEmail(email);
|
||||
imported.setFirstName(picketlinkUser.getFirstName());
|
||||
imported.setLastName(picketlinkUser.getLastName());
|
||||
imported.setFederationLink(model.getId());
|
||||
return imported;
|
||||
}
|
||||
|
||||
protected User queryByEmail(IdentityManager identityManager, String email) throws IdentityManagementException {
|
||||
List<User> agents = identityManager.createIdentityQuery(User.class)
|
||||
.setParameter(User.EMAIL, email).getResultList();
|
||||
|
||||
if (agents.isEmpty()) {
|
||||
return null;
|
||||
} else if (agents.size() == 1) {
|
||||
return agents.get(0);
|
||||
} else {
|
||||
throw new IdentityManagementException("Error - multiple Agent objects found with same login name");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = queryByEmail(identityManager, email);
|
||||
if (picketlinkUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return importUserFromPicketlink(realm, picketlinkUser);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public boolean validPassword(String username, String password) {
|
||||
IdentityManager identityManager = getIdentityManager();
|
||||
|
||||
try {
|
||||
UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
|
||||
credential.setUsername(username);
|
||||
credential.setPassword(new Password(password.toCharArray()));
|
||||
identityManager.validateCredentials(credential);
|
||||
if (credential.getStatus() == Credentials.Status.VALID) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw convertIDMException(ie);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||
for (UserCredentialModel cred : input) {
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
return validPassword(user.getUsername(), cred.getValue());
|
||||
} else {
|
||||
return false; // invalid cred type
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||
for (UserCredentialModel cred : input) {
|
||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
return validPassword(user.getUsername(), cred.getValue());
|
||||
} else {
|
||||
return false; // invalid cred type
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.FederationProvider;
|
||||
import org.keycloak.models.FederationProviderFactory;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.PartitionManager;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LDAPFederationProviderFactory implements FederationProviderFactory {
|
||||
PartitionManagerRegistry registry;
|
||||
|
||||
@Override
|
||||
public FederationProvider create(KeycloakSession session) {
|
||||
throw new IllegalAccessError("Illegal to call this method");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FederationProvider getInstance(KeycloakSession session, FederationProviderModel model) {
|
||||
PartitionManager partition = registry.getPartitionManager(model);
|
||||
return new LDAPFederationProvider(session, model, partition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
registry = new PartitionManagerRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "ldap";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.keycloak.models.utils.reflection.Reflections;
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.config.LDAPMappingConfiguration;
|
||||
import org.picketlink.idm.credential.Credentials;
|
||||
import org.picketlink.idm.credential.Password;
|
||||
import org.picketlink.idm.credential.UsernamePasswordCredentials;
|
||||
import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
|
||||
import org.picketlink.idm.ldap.internal.LDAPOperationManager;
|
||||
import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
|
||||
import org.picketlink.idm.model.Account;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
import org.picketlink.idm.query.IdentityQuery;
|
||||
import org.picketlink.idm.spi.IdentityContext;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.ModificationItem;
|
||||
import javax.naming.directory.SearchResult;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
|
||||
import static org.picketlink.idm.model.basic.BasicModel.getUser;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
|
||||
|
||||
// Used just in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
|
||||
private String userAccountControlAfterPasswordUpdate;
|
||||
|
||||
@Override
|
||||
public void setup(LDAPIdentityStore store) {
|
||||
// TODO: Don't setup it here once PLINK-508 is fixed
|
||||
if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
|
||||
String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
|
||||
this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
|
||||
CREDENTIAL_LOGGER.info("Will use userAccountControl=" + userAccountControlAfterPasswordUpdate + " after password update of user in Active Directory");
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden as in Keycloak, we don't have Agents
|
||||
@Override
|
||||
protected User getAccount(IdentityContext context, String loginName) {
|
||||
IdentityManager identityManager = getIdentityManager(context);
|
||||
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Trying to find account [%s] using default account type [%s]", loginName, User.class);
|
||||
}
|
||||
|
||||
return getUser(identityManager, loginName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(IdentityContext context, Account account, Password password, LDAPIdentityStore store, Date effectiveDate, Date expiryDate) {
|
||||
if (!store.getConfig().isActiveDirectory()) {
|
||||
super.update(context, account, password, store, effectiveDate, expiryDate);
|
||||
} else {
|
||||
User user = (User)account;
|
||||
LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
|
||||
IdentityManager identityManager = getIdentityManager(context);
|
||||
String userDN = getDNOfUser(user, identityManager, store, operationManager);
|
||||
|
||||
updateADPassword(userDN, new String(password.getValue()), operationManager);
|
||||
|
||||
if (userAccountControlAfterPasswordUpdate != null) {
|
||||
ModificationItem[] mods = new ModificationItem[1];
|
||||
BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
|
||||
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
|
||||
operationManager.modifyAttribute(userDN, mod0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateADPassword(String userDN, String password, LDAPOperationManager operationManager) {
|
||||
try {
|
||||
// Replace the "unicdodePwd" attribute with a new value
|
||||
// Password must be both Unicode and a quoted string
|
||||
String newQuotedPassword = "\"" + password + "\"";
|
||||
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
|
||||
BasicAttribute unicodePwd = new BasicAttribute("unicodePwd", newUnicodePassword);
|
||||
operationManager.modifyAttribute(userDN, unicodePwd);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(IdentityContext context, UsernamePasswordCredentials credentials,
|
||||
LDAPIdentityStore store) {
|
||||
credentials.setStatus(Credentials.Status.INVALID);
|
||||
credentials.setValidatedAccount(null);
|
||||
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Validating credentials [%s][%s] using identity store [%s] and credential handler [%s].", credentials.getClass(), credentials, store, this);
|
||||
}
|
||||
|
||||
User account = getAccount(context, credentials.getUsername());
|
||||
|
||||
// If the user for the provided username cannot be found we fail validation
|
||||
if (account != null) {
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Found account [%s] from credentials [%s].", account, credentials);
|
||||
}
|
||||
|
||||
if (account.isEnabled()) {
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Account [%s] is ENABLED.", account, credentials);
|
||||
}
|
||||
|
||||
char[] password = credentials.getPassword().getValue();
|
||||
|
||||
// String bindingDN = store.getBindingDN(account);
|
||||
|
||||
LDAPOperationManager operationManager = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.GET_OPERATION_MANAGER_METHOD, LDAPOperationManager.class, store);
|
||||
String bindingDN = getDNOfUser(account, getIdentityManager(context), store, operationManager);
|
||||
|
||||
if (operationManager.authenticate(bindingDN, new String(password))) {
|
||||
credentials.setValidatedAccount(account);
|
||||
credentials.setStatus(Credentials.Status.VALID);
|
||||
}
|
||||
} else {
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Account [%s] is DISABLED.", account, credentials);
|
||||
}
|
||||
credentials.setStatus(Credentials.Status.ACCOUNT_DISABLED);
|
||||
}
|
||||
} else {
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Account NOT FOUND for credentials [%s][%s].", credentials.getClass(), credentials);
|
||||
}
|
||||
}
|
||||
|
||||
if (CREDENTIAL_LOGGER.isDebugEnabled()) {
|
||||
CREDENTIAL_LOGGER.debugf("Credential [%s][%s] validated using identity store [%s] and credential handler [%s]. Status [%s]. Validated Account [%s]",
|
||||
credentials.getClass(), credentials, store, this, credentials.getStatus(), credentials.getValidatedAccount());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove later... It's needed just because LDAPIdentityStore.getBindingDN, which always uses idProperty as first part of DN, but in AD it doesn't work as we may have idProperty 'sAMAccountName'
|
||||
// but DN like: cn=John Doe,OU=foo,DC=bar
|
||||
protected String getDNOfUser(User user, IdentityManager identityManager, LDAPIdentityStore ldapStore, LDAPOperationManager operationManager) {
|
||||
|
||||
LDAPMappingConfiguration ldapEntryConfig = ldapStore.getConfig().getMappingConfig(User.class);
|
||||
IdentityQuery<User> identityQuery = identityManager.createIdentityQuery(User.class)
|
||||
.setParameter(User.LOGIN_NAME, user.getLoginName());
|
||||
StringBuilder filter = Reflections.invokeMethod(false, KeycloakLDAPIdentityStore.CREATE_SEARCH_FILTER_METHOD, StringBuilder.class, ldapStore, identityQuery, ldapEntryConfig);
|
||||
|
||||
List<SearchResult> search = null;
|
||||
try {
|
||||
search = operationManager.search(ldapEntryConfig.getBaseDN(), filter.toString(), ldapEntryConfig);
|
||||
} catch (NamingException ne) {
|
||||
throw new RuntimeException(ne);
|
||||
}
|
||||
|
||||
if (search.size() > 0) {
|
||||
SearchResult sr1 = search.get(0);
|
||||
return sr1.getNameInNamespace();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.picketlink.idm.IdentityManagementException;
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.model.basic.BasicModel;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LDAPUserModelDelegate {
|
||||
protected UserModel delegate;
|
||||
protected LDAPFederationProvider provider;
|
||||
|
||||
public String getId() {
|
||||
return delegate.getId();
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
delegate.setAttribute(name, value);
|
||||
}
|
||||
|
||||
public boolean isEmailVerified() {
|
||||
return delegate.isEmailVerified();
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
delegate.removeAttribute(name);
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return delegate.getLastName();
|
||||
}
|
||||
|
||||
public void setFederationLink(String link) {
|
||||
delegate.setFederationLink(link);
|
||||
}
|
||||
|
||||
public AuthenticationLinkModel getAuthenticationLink() {
|
||||
return delegate.getAuthenticationLink();
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return delegate.getAttributes();
|
||||
}
|
||||
|
||||
public boolean hasRole(RoleModel role) {
|
||||
return delegate.hasRole(role);
|
||||
}
|
||||
|
||||
public void grantRole(RoleModel role) {
|
||||
delegate.grantRole(role);
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
delegate.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public void removeRequiredAction(UserModel.RequiredAction action) {
|
||||
delegate.removeRequiredAction(action);
|
||||
}
|
||||
|
||||
public void deleteRoleMapping(RoleModel role) {
|
||||
delegate.deleteRoleMapping(role);
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
IdentityManager identityManager = provider.getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||
if (picketlinkUser == null) {
|
||||
throw new IllegalStateException("User not found in LDAP storage!");
|
||||
}
|
||||
picketlinkUser.setLoginName(username);
|
||||
identityManager.update(picketlinkUser);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw new ModelException(ie);
|
||||
}
|
||||
delegate.setUsername(username);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return delegate.isEnabled();
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return delegate.getFirstName();
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
IdentityManager identityManager = provider.getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||
if (picketlinkUser == null) {
|
||||
throw new IllegalStateException("User not found in LDAP storage!");
|
||||
}
|
||||
picketlinkUser.setLastName(lastName);
|
||||
identityManager.update(picketlinkUser);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw new ModelException(ie);
|
||||
}
|
||||
delegate.setLastName(lastName);
|
||||
}
|
||||
|
||||
public void setEmailVerified(boolean verified) {
|
||||
delegate.setEmailVerified(verified);
|
||||
}
|
||||
|
||||
public void updateCredential(UserCredentialModel cred) {
|
||||
delegate.updateCredential(cred);
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
IdentityManager identityManager = provider.getIdentityManager();
|
||||
|
||||
try {
|
||||
User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername());
|
||||
if (picketlinkUser == null) {
|
||||
throw new IllegalStateException("User not found in LDAP storage!");
|
||||
}
|
||||
picketlinkUser.setEmail(email);
|
||||
identityManager.update(picketlinkUser);
|
||||
} catch (IdentityManagementException ie) {
|
||||
throw new ModelException(ie);
|
||||
}
|
||||
delegate.setEmail(email);
|
||||
}
|
||||
|
||||
public void addRequiredAction(UserModel.RequiredAction action) {
|
||||
delegate.addRequiredAction(action);
|
||||
}
|
||||
|
||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||
return delegate.getCredentialsDirectly();
|
||||
}
|
||||
|
||||
public boolean isTotp() {
|
||||
return delegate.isTotp();
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
delegate.setFirstName(firstName);
|
||||
}
|
||||
|
||||
public Set<UserModel.RequiredAction> getRequiredActions() {
|
||||
return delegate.getRequiredActions();
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return delegate.getEmail();
|
||||
}
|
||||
|
||||
public void setTotp(boolean totp) {
|
||||
delegate.setTotp(totp);
|
||||
}
|
||||
|
||||
public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
|
||||
delegate.setAuthenticationLink(authenticationLink);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return delegate.getUsername();
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return delegate.getFederationLink();
|
||||
}
|
||||
|
||||
public Set<RoleModel> getRealmRoleMappings() {
|
||||
return delegate.getRealmRoleMappings();
|
||||
}
|
||||
|
||||
public Set<RoleModel> getRoleMappings() {
|
||||
return delegate.getRoleMappings();
|
||||
}
|
||||
|
||||
public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
|
||||
return delegate.getApplicationRoleMappings(app);
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return delegate.getAttribute(name);
|
||||
}
|
||||
|
||||
public void updateCredentialDirectly(UserCredentialValueModel cred) {
|
||||
delegate.updateCredentialDirectly(cred);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package org.keycloak.federation.ldap;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.picketlink.idm.PartitionManager;
|
||||
import org.picketlink.idm.config.AbstractIdentityStoreConfiguration;
|
||||
import org.picketlink.idm.config.IdentityConfiguration;
|
||||
import org.picketlink.idm.config.IdentityConfigurationBuilder;
|
||||
import org.picketlink.idm.config.IdentityStoreConfiguration;
|
||||
import org.picketlink.idm.config.LDAPIdentityStoreConfiguration;
|
||||
import org.picketlink.idm.internal.DefaultPartitionManager;
|
||||
import org.picketlink.idm.model.basic.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.picketlink.common.constants.LDAPConstants.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PartitionManagerRegistry {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PartitionManagerRegistry.class);
|
||||
|
||||
private Map<String, PartitionManagerContext> partitionManagers = new ConcurrentHashMap<String, PartitionManagerContext>();
|
||||
|
||||
public PartitionManager getPartitionManager(FederationProviderModel model) {
|
||||
PartitionManagerContext context = partitionManagers.get(model.getId());
|
||||
|
||||
// Ldap config might have changed for the realm. In this case, we must re-initialize
|
||||
Map<String, String> config = model.getConfig();
|
||||
if (context == null || !config.equals(context.config)) {
|
||||
logger.infof("Creating new partition manager for the federation provider: %s, LDAP Connection URL: %s, LDAP Base DN: %s, LDAP Vendor: %s", model.getId(),
|
||||
config.get(LDAPConstants.CONNECTION_URL), config.get(LDAPConstants.BASE_DN), config.get(LDAPConstants.VENDOR));
|
||||
PartitionManager manager = createPartitionManager(config);
|
||||
context = new PartitionManagerContext(config, manager);
|
||||
partitionManagers.put(model.getId(), context);
|
||||
}
|
||||
return context.partitionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ldapConfig from realm
|
||||
* @return PartitionManager instance based on LDAP store
|
||||
*/
|
||||
protected PartitionManager createPartitionManager(Map<String,String> ldapConfig) {
|
||||
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();
|
||||
|
||||
Properties connectionProps = new Properties();
|
||||
connectionProps.put("com.sun.jndi.ldap.connect.pool", "true");
|
||||
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.authentication", "none simple");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.initsize", "1");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.maxsize", "10");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.prefsize", "5");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.timeout", "300000");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
|
||||
checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
|
||||
|
||||
String vendor = ldapConfig.get(LDAPConstants.VENDOR);
|
||||
|
||||
// RHDS is using "nsuniqueid" as unique identifier instead of "entryUUID"
|
||||
if (vendor != null && vendor.equals(LDAPConstants.VENDOR_RHDS)) {
|
||||
checkSystemProperty(LDAPIdentityStoreConfiguration.ENTRY_IDENTIFIER_ATTRIBUTE_NAME, "nsuniqueid");
|
||||
}
|
||||
|
||||
boolean activeDirectory = vendor != null && vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY);
|
||||
|
||||
String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE);
|
||||
if (ldapLoginNameMapping == null) {
|
||||
ldapLoginNameMapping = activeDirectory ? CN : UID;
|
||||
}
|
||||
|
||||
// Try to compute properties based on LDAP server type, but still allow to override them through System properties TODO: Should allow better way than overriding from System properties. Perhaps init from XML?
|
||||
ldapLoginNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", ldapLoginNameMapping, ldapLoginNameMapping, activeDirectory);
|
||||
String ldapFirstNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
|
||||
String ldapLastNameMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
|
||||
String ldapEmailMapping = getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
|
||||
|
||||
String[] userObjectClasses = getUserObjectClasses(ldapConfig);
|
||||
|
||||
logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginNameMapping, ldapFirstNameMapping, ldapLastNameMapping, ldapEmailMapping);
|
||||
|
||||
// Use same mapping for User and Agent for now
|
||||
builder
|
||||
.named("SIMPLE_LDAP_STORE_CONFIG")
|
||||
.stores()
|
||||
.ldap()
|
||||
.connectionProperties(connectionProps)
|
||||
.addCredentialHandler(LDAPKeycloakCredentialHandler.class)
|
||||
.baseDN(ldapConfig.get(LDAPConstants.BASE_DN))
|
||||
.bindDN(ldapConfig.get(LDAPConstants.BIND_DN))
|
||||
.bindCredential(ldapConfig.get(LDAPConstants.BIND_CREDENTIAL))
|
||||
.url(ldapConfig.get(LDAPConstants.CONNECTION_URL))
|
||||
.activeDirectory(activeDirectory)
|
||||
.supportAllFeatures()
|
||||
.mapping(User.class)
|
||||
.baseDN(ldapConfig.get(LDAPConstants.USER_DN_SUFFIX))
|
||||
.objectClasses(userObjectClasses)
|
||||
.attribute("loginName", ldapLoginNameMapping, true)
|
||||
.attribute("firstName", ldapFirstNameMapping)
|
||||
.attribute("lastName", ldapLastNameMapping)
|
||||
.attribute("email", ldapEmailMapping);
|
||||
|
||||
// Workaround to override the LDAPIdentityStore with our own :/
|
||||
List<IdentityConfiguration> identityConfigs = builder.buildAll();
|
||||
IdentityStoreConfiguration identityStoreConfig = identityConfigs.get(0).getStoreConfiguration().get(0);
|
||||
((AbstractIdentityStoreConfiguration)identityStoreConfig).setIdentityStoreType(KeycloakLDAPIdentityStore.class);
|
||||
|
||||
return new DefaultPartitionManager(identityConfigs);
|
||||
}
|
||||
|
||||
private void checkSystemProperty(String name, String defaultValue) {
|
||||
if (System.getProperty(name) == null) {
|
||||
System.setProperty(name, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
private String getNameOfLDAPAttribute(String systemPropertyName, String defaultAttrName, String defaultAttrNameInActiveDirectory, boolean activeDirectory) {
|
||||
// System property has biggest priority if available
|
||||
String sysProperty = System.getProperty(systemPropertyName);
|
||||
if (sysProperty != null) {
|
||||
return sysProperty;
|
||||
}
|
||||
|
||||
return activeDirectory ? defaultAttrNameInActiveDirectory : defaultAttrName;
|
||||
}
|
||||
|
||||
// Parse array of strings like [ "inetOrgPerson", "organizationalPerson" ] from the string like: "inetOrgPerson, organizationalPerson"
|
||||
private String[] getUserObjectClasses(Map<String,String> ldapConfig) {
|
||||
String objClassesCfg = ldapConfig.get(LDAPConstants.USER_OBJECT_CLASSES);
|
||||
String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson, organizationalPerson";
|
||||
|
||||
String[] objectClasses = objClassesStr.split(",");
|
||||
|
||||
// Trim them
|
||||
String[] userObjectClasses = new String[objectClasses.length];
|
||||
for (int i=0 ; i<objectClasses.length ; i++) {
|
||||
userObjectClasses[i] = objectClasses[i].trim();
|
||||
}
|
||||
return userObjectClasses;
|
||||
}
|
||||
|
||||
private class PartitionManagerContext {
|
||||
|
||||
private PartitionManagerContext(Map<String,String> config, PartitionManager manager) {
|
||||
this.config = config;
|
||||
this.partitionManager = manager;
|
||||
}
|
||||
|
||||
private Map<String,String> config;
|
||||
private PartitionManager partitionManager;
|
||||
}
|
||||
}
|
22
federation/pom.xml
Executable file
22
federation/pom.xml
Executable file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<artifactId>keycloak-federation-parent</artifactId>
|
||||
<name>Keycloak Federation</name>
|
||||
<description />
|
||||
|
||||
<modules>
|
||||
<module>ldap</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
349
model/api/src/main/java/org/keycloak/models/FederationManager.java
Executable file
349
model/api/src/main/java/org/keycloak/models/FederationManager.java
Executable file
|
@ -0,0 +1,349 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FederationManager implements UserProvider {
|
||||
protected KeycloakSession session;
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
|
||||
UserModel user = session.userStorage().addUser(realm, id, username, addDefaultRoles);
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
return fed.addUser(realm, user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
protected FederationProvider getFederationProvider(FederationProviderModel model) {
|
||||
FederationProviderFactory factory = (FederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(FederationProvider.class, model.getProviderName());
|
||||
return factory.getInstance(session, model);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(RealmModel realm, String username) {
|
||||
UserModel user = session.userStorage().addUser(realm, username);
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = getFederationProvider(federation);
|
||||
return fed.addUser(realm, user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
protected FederationProvider getFederationLink(RealmModel realm, UserModel user) {
|
||||
if (user.getFederationLink() == null) return null;
|
||||
for (FederationProviderModel fed : realm.getFederationProviders()) {
|
||||
if (fed.getId().equals(user.getFederationLink())) {
|
||||
return getFederationProvider(fed);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeUser(RealmModel realm, String name) {
|
||||
UserModel user = session.userStorage().getUserByUsername(name, realm);
|
||||
if (user == null) return false;
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.removeUser(realm, user);
|
||||
}
|
||||
return session.userStorage().removeUser(realm, name);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
link.addSocialLink(realm, user, socialLink);
|
||||
return;
|
||||
}
|
||||
session.userStorage().addSocialLink(realm, user, socialLink);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.removeSocialLink(realm, user, socialProvider);
|
||||
}
|
||||
return session.userStorage().removeSocialLink(realm, user, socialProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserById(id, realm);
|
||||
if (user != null) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.proxy(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
user = fed.getUserById(id, realm);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserByUsername(username, realm);
|
||||
if (user != null) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.proxy(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
user = fed.getUserByUsername(username, realm);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserByEmail(email, realm);
|
||||
if (user != null) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.proxy(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
user = fed.getUserByEmail(email, realm);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
|
||||
UserModel user = session.userStorage().getUserBySocialLink(socialLink, realm);
|
||||
if (user != null) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.proxy(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = getFederationProvider(federation);
|
||||
user = fed.getUserBySocialLink(socialLink, realm);
|
||||
if (user != null) return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm) {
|
||||
return getUsers(realm, 0, Integer.MAX_VALUE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUsersCount(RealmModel realm) {
|
||||
return session.userStorage().getUsersCount(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
|
||||
Map<String, UserModel> users = new HashMap<String, UserModel>();
|
||||
List<UserModel> query = session.userStorage().getUsers(realm, firstResult, maxResults);
|
||||
for (UserModel user : query) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
users.put(user.getUsername(), link.proxy(user));
|
||||
} else {
|
||||
users.put(user.getUsername(), user);
|
||||
}
|
||||
}
|
||||
if (users.size() >= maxResults) {
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
List<FederationProviderModel> federationProviders = realm.getFederationProviders();
|
||||
for (int i = federationProviders.size(); i >= 0; i--) {
|
||||
FederationProviderModel federation = federationProviders.get(i);
|
||||
FederationProvider fed = getFederationProvider(federation);
|
||||
query = fed.getUsers(realm, firstResult, maxResults);
|
||||
for (UserModel user : query) users.put(user.getUsername(), user);
|
||||
}
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm) {
|
||||
return searchForUser(search, realm, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
|
||||
Map<String, UserModel> users = new HashMap<String, UserModel>();
|
||||
List<UserModel> query = session.userStorage().searchForUser(search, realm, firstResult, maxResults);
|
||||
for (UserModel user : query) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
users.put(user.getUsername(), link.proxy(user));
|
||||
} else {
|
||||
users.put(user.getUsername(), user);
|
||||
}
|
||||
}
|
||||
if (users.size() >= maxResults) {
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
List<FederationProviderModel> federationProviders = realm.getFederationProviders();
|
||||
for (int i = federationProviders.size(); i >= 0; i--) {
|
||||
FederationProviderModel federation = federationProviders.get(i);
|
||||
FederationProvider fed = getFederationProvider(federation);
|
||||
query = fed.searchForUser(search, realm, firstResult, maxResults);
|
||||
for (UserModel user : query) users.put(user.getUsername(), user);
|
||||
}
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
|
||||
return searchForUserByAttributes(attributes, realm, 0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||
Map<String, UserModel> users = new HashMap<String, UserModel>();
|
||||
List<UserModel> query = session.userStorage().searchForUserByAttributes(attributes, realm, firstResult, maxResults);
|
||||
for (UserModel user : query) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
users.put(user.getUsername(), link.proxy(user));
|
||||
} else {
|
||||
users.put(user.getUsername(), user);
|
||||
}
|
||||
}
|
||||
if (users.size() >= maxResults) {
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
List<FederationProviderModel> federationProviders = realm.getFederationProviders();
|
||||
for (int i = federationProviders.size(); i >= 0; i--) {
|
||||
FederationProviderModel federation = federationProviders.get(i);
|
||||
FederationProvider fed = getFederationProvider(federation);
|
||||
query = fed.searchForUserByAttributes(attributes, realm, firstResult, maxResults);
|
||||
for (UserModel user : query) users.put(user.getUsername(), user);
|
||||
}
|
||||
List<UserModel> results = new ArrayList<UserModel>(users.size());
|
||||
results.addAll(users.values());
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.getSocialLinks(user, realm);
|
||||
}
|
||||
return session.userStorage().getSocialLinks(user, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
return link.getSocialLink(user, socialProvider, realm);
|
||||
}
|
||||
return session.userStorage().getSocialLink(user, socialProvider, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm) {
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
fed.preRemove(realm);
|
||||
}
|
||||
session.userStorage().preRemove(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
for (FederationProviderModel federation : realm.getFederationProviders()) {
|
||||
FederationProvider fed = session.getProvider(FederationProvider.class, federation.getProviderName());
|
||||
fed.preRemove(realm, role);
|
||||
}
|
||||
session.userStorage().preRemove(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
if (link.getSupportedCredentialTypes().size() > 0) {
|
||||
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
||||
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
||||
for (UserCredentialModel cred : input) {
|
||||
if (fedCreds.contains(cred.getType())) {
|
||||
fedCreds.add(cred);
|
||||
} else {
|
||||
localCreds.add(cred);
|
||||
}
|
||||
}
|
||||
if (!link.validCredentials(realm, user, fedCreds)) {
|
||||
return false;
|
||||
}
|
||||
return session.userStorage().validCredentials(realm, user, localCreds);
|
||||
}
|
||||
}
|
||||
return session.userStorage().validCredentials(realm, user, input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||
FederationProvider link = getFederationLink(realm, user);
|
||||
if (link != null) {
|
||||
if (link.getSupportedCredentialTypes().size() > 0) {
|
||||
List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
|
||||
List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
|
||||
for (UserCredentialModel cred : input) {
|
||||
if (fedCreds.contains(cred.getType())) {
|
||||
fedCreds.add(cred);
|
||||
} else {
|
||||
localCreds.add(cred);
|
||||
}
|
||||
}
|
||||
if (!link.validCredentials(realm, user, fedCreds)) {
|
||||
return false;
|
||||
}
|
||||
return session.userStorage().validCredentials(realm, user, localCreds);
|
||||
}
|
||||
}
|
||||
return session.userStorage().validCredentials(realm, user, input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
17
model/api/src/main/java/org/keycloak/models/FederationProvider.java
Executable file
17
model/api/src/main/java/org/keycloak/models/FederationProvider.java
Executable file
|
@ -0,0 +1,17 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface FederationProvider extends UserProvider {
|
||||
UserModel proxy(UserModel local);
|
||||
UserModel addUser(RealmModel realm, UserModel user);
|
||||
boolean removeUser(RealmModel realm, UserModel user);
|
||||
Set<String> getSupportedCredentialTypes();
|
||||
String getAdminPage();
|
||||
Class getAdminClass();
|
||||
}
|
11
model/api/src/main/java/org/keycloak/models/FederationProviderFactory.java
Executable file
11
model/api/src/main/java/org/keycloak/models/FederationProviderFactory.java
Executable file
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface FederationProviderFactory extends ProviderFactory<FederationProvider> {
|
||||
FederationProvider getInstance(KeycloakSession session, FederationProviderModel model);
|
||||
}
|
45
model/api/src/main/java/org/keycloak/models/FederationProviderModel.java
Executable file
45
model/api/src/main/java/org/keycloak/models/FederationProviderModel.java
Executable file
|
@ -0,0 +1,45 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class FederationProviderModel {
|
||||
|
||||
private String id;
|
||||
private String providerName;
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
public FederationProviderModel() {};
|
||||
|
||||
public FederationProviderModel(String id, String providerName, Map<String, String> config) {
|
||||
this.id = id;
|
||||
this.providerName = providerName;
|
||||
if (config != null) {
|
||||
this.config.putAll(config);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ public interface KeycloakSession {
|
|||
|
||||
<T extends Provider> Set<T> getAllProviders(Class<T> clazz);
|
||||
|
||||
KeycloakSessionFactory getKeycloakSessionFactory();
|
||||
|
||||
/**
|
||||
* Returns a managed provider instance. Will start a provider transaction. This transaction is managed by the KeycloakSession
|
||||
* transaction.
|
||||
|
@ -38,7 +40,19 @@ public interface KeycloakSession {
|
|||
*/
|
||||
UserSessionProvider sessions();
|
||||
|
||||
|
||||
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Possibly both cached and federated view of users depending on configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
UserProvider users();
|
||||
|
||||
/**
|
||||
* Keycloak user storage. Non-federated, but possibly cache (if it is on) view of users.
|
||||
*/
|
||||
UserProvider userStorage();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface KeycloakSessionFactory {
|
||||
KeycloakSession create();
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz);
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
|
||||
|
||||
void close();
|
||||
}
|
||||
|
|
|
@ -165,6 +165,10 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders);
|
||||
|
||||
List<FederationProviderModel> getFederationProviders();
|
||||
|
||||
void setFederationProviders(List<FederationProviderModel> providers);
|
||||
|
||||
String getLoginTheme();
|
||||
|
||||
void setLoginTheme(String name);
|
||||
|
|
|
@ -77,6 +77,9 @@ public interface UserModel {
|
|||
Set<RoleModel> getRoleMappings();
|
||||
void deleteRoleMapping(RoleModel role);
|
||||
|
||||
String getFederationLink();
|
||||
void setFederationLink(String link);
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public interface UserProvider extends Provider {
|
|||
SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm);
|
||||
|
||||
void preRemove(RealmModel realm);
|
||||
void preRemove(RoleModel role);
|
||||
void preRemove(RealmModel realm, RoleModel role);
|
||||
|
||||
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
||||
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FederationProviderEntity {
|
||||
protected String id;
|
||||
protected String providerName;
|
||||
private Map<String, String> config;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
|
@ -51,6 +51,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
|
||||
private List<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
||||
private List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
||||
private List<FederationProviderEntity> federationProviders = new ArrayList<FederationProviderEntity>();
|
||||
|
||||
private Map<String, String> smtpConfig = new HashMap<String, String>();
|
||||
private Map<String, String> socialConfig = new HashMap<String, String>();
|
||||
|
@ -381,4 +382,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
public void setAdminAppId(String adminAppId) {
|
||||
this.adminAppId = adminAppId;
|
||||
}
|
||||
|
||||
public List<FederationProviderEntity> getFederationProviders() {
|
||||
return federationProviders;
|
||||
}
|
||||
|
||||
public void setFederationProviders(List<FederationProviderEntity> federationProviders) {
|
||||
this.federationProviders = federationProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
|||
private List<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
|
||||
private List<SocialLinkEntity> socialLinks;
|
||||
private AuthenticationLinkEntity authenticationLink;
|
||||
private String federationLink;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
|
@ -140,5 +141,13 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
|||
public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
|
||||
this.authenticationLink = authenticationLink;
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.AuthenticationProviderModel;
|
|||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
|
@ -13,6 +14,7 @@ import org.keycloak.models.SocialLinkModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.FederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ClaimRepresentation;
|
||||
|
@ -141,6 +143,18 @@ public class ModelToRepresentation {
|
|||
}
|
||||
rep.setAuthenticationProviders(authProviderReps);
|
||||
}
|
||||
List<FederationProviderModel> fedProviderModels = realm.getFederationProviders();
|
||||
if (fedProviderModels.size() > 0) {
|
||||
List<FederationProviderRepresentation> fedProviderReps = new ArrayList<FederationProviderRepresentation>();
|
||||
for (FederationProviderModel model : fedProviderModels) {
|
||||
FederationProviderRepresentation fedProvRep = new FederationProviderRepresentation();
|
||||
fedProvRep.setId(model.getId());
|
||||
fedProvRep.setProviderName(model.getProviderName());
|
||||
fedProvRep.setConfig(model.getConfig());
|
||||
fedProviderReps.add(fedProvRep);
|
||||
}
|
||||
rep.setFederationProviders(fedProviderReps);
|
||||
}
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.keycloak.models.AuthenticationLinkModel;
|
|||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClaimMask;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
|
@ -16,6 +17,7 @@ import org.keycloak.models.SocialLinkModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.FederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
|
||||
|
@ -213,6 +215,11 @@ public class RepresentationToModel {
|
|||
newRealm.setAuthenticationProviders(authProviderModels);
|
||||
}
|
||||
|
||||
if (rep.getFederationProviders() != null) {
|
||||
List<FederationProviderModel> providerModels = convertFederationProviders(rep.getFederationProviders());
|
||||
newRealm.setFederationProviders(providerModels);
|
||||
}
|
||||
|
||||
// create users and their role mappings and social mappings
|
||||
|
||||
if (rep.getUsers() != null) {
|
||||
|
@ -280,6 +287,11 @@ public class RepresentationToModel {
|
|||
realm.setAuthenticationProviders(authProviderModels);
|
||||
}
|
||||
|
||||
if (rep.getFederationProviders() != null) {
|
||||
List<FederationProviderModel> providerModels = convertFederationProviders(rep.getFederationProviders());
|
||||
realm.setFederationProviders(providerModels);
|
||||
}
|
||||
|
||||
if ("GENERATE".equals(rep.getPublicKey())) {
|
||||
KeycloakModelUtils.generateRealmKeys(realm);
|
||||
}
|
||||
|
@ -303,6 +315,17 @@ public class RepresentationToModel {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static List<FederationProviderModel> convertFederationProviders(List<FederationProviderRepresentation> providers) {
|
||||
List<FederationProviderModel> result = new ArrayList<FederationProviderModel>();
|
||||
|
||||
for (FederationProviderRepresentation representation : providers) {
|
||||
FederationProviderModel model = new FederationProviderModel(representation.getId(), representation.getProviderName(),
|
||||
representation.getConfig());
|
||||
result.add(model);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Roles
|
||||
|
||||
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
|
||||
|
@ -584,6 +607,7 @@ public class RepresentationToModel {
|
|||
user.setEmail(userRep.getEmail());
|
||||
user.setFirstName(userRep.getFirstName());
|
||||
user.setLastName(userRep.getLastName());
|
||||
user.setFederationLink(userRep.getFederationLink());
|
||||
if (userRep.getAttributes() != null) {
|
||||
for (Map.Entry<String, String> entry : userRep.getAttributes().entrySet()) {
|
||||
user.setAttribute(entry.getKey(), entry.getValue());
|
||||
|
|
|
@ -283,7 +283,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RoleModel role) {
|
||||
getDelegate().preRemove(role);
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
getDelegate().preRemove(realm, role);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public class NoCacheUserProvider implements CacheUserProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RoleModel role) {
|
||||
getDelegate().preRemove(role);
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
getDelegate().preRemove(realm, role);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.cache;
|
|||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.utils.CredentialValidation;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
|
@ -608,6 +609,18 @@ public class RealmAdapter implements RealmModel {
|
|||
updated.setAuthenticationProviders(authenticationProviders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FederationProviderModel> getFederationProviders() {
|
||||
if (updated != null) return updated.getFederationProviders();
|
||||
return cached.getFederationProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationProviders(List<FederationProviderModel> providers) {
|
||||
getDelegateForUpdate();
|
||||
updated.setFederationProviders(providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginTheme() {
|
||||
if (updated != null) return updated.getLoginTheme();
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models.cache;
|
|||
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
|
@ -203,6 +204,18 @@ public class UserAdapter implements UserModel {
|
|||
updated.setAuthenticationLink(authenticationLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
if (updated != null) return updated.getFederationLink();
|
||||
return cached.getFederationLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationLink(String link) {
|
||||
getDelegateForUpdate();
|
||||
updated.setFederationLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoleMappings() {
|
||||
if (updated != null) return updated.getRealmRoleMappings();
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models.cache.entities;
|
|||
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
|
@ -64,6 +65,7 @@ public class CachedRealm {
|
|||
|
||||
private List<RequiredCredentialModel> requiredCredentials = new ArrayList<RequiredCredentialModel>();
|
||||
private List<AuthenticationProviderModel> authenticationProviders = new ArrayList<AuthenticationProviderModel>();
|
||||
private List<FederationProviderModel> federationProviders = new ArrayList<FederationProviderModel>();
|
||||
|
||||
private Map<String, String> smtpConfig = new HashMap<String, String>();
|
||||
private Map<String, String> socialConfig = new HashMap<String, String>();
|
||||
|
@ -120,6 +122,7 @@ public class CachedRealm {
|
|||
|
||||
requiredCredentials = model.getRequiredCredentials();
|
||||
authenticationProviders = model.getAuthenticationProviders();
|
||||
federationProviders = model.getFederationProviders();
|
||||
|
||||
smtpConfig.putAll(model.getSmtpConfig());
|
||||
socialConfig.putAll(model.getSocialConfig());
|
||||
|
@ -328,4 +331,7 @@ public class CachedRealm {
|
|||
return auditListeners;
|
||||
}
|
||||
|
||||
public List<FederationProviderModel> getFederationProviders() {
|
||||
return federationProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.models.cache.entities;
|
||||
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
|
@ -28,6 +29,7 @@ public class CachedUser {
|
|||
private boolean enabled;
|
||||
private boolean totp;
|
||||
private AuthenticationLinkModel authenticationLink;
|
||||
private String federationLink;
|
||||
private Map<String, String> attributes = new HashMap<String, String>();
|
||||
private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
|
||||
private Set<String> roleMappings = new HashSet<String>();
|
||||
|
@ -44,6 +46,7 @@ public class CachedUser {
|
|||
this.credentials.addAll(user.getCredentialsDirectly());
|
||||
this.enabled = user.isEnabled();
|
||||
this.totp = user.isTotp();
|
||||
this.federationLink = user.getFederationLink();
|
||||
this.requiredActions.addAll(user.getRequiredActions());
|
||||
this.authenticationLink = user.getAuthenticationLink();
|
||||
for (RoleModel role : user.getRoleMappings()) {
|
||||
|
@ -102,4 +105,8 @@ public class CachedUser {
|
|||
public AuthenticationLinkModel getAuthenticationLink() {
|
||||
return authenticationLink;
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
|
|||
}
|
||||
if (!roleModel.getContainer().equals(this)) return false;
|
||||
|
||||
session.users().preRemove(roleModel);
|
||||
session.users().preRemove(getRealm(), roleModel);
|
||||
RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
|
||||
if (!role.isApplicationRole()) return false;
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ public class JpaUserProvider implements UserProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RoleModel role) {
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
em.createNamedQuery("deleteUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.keycloak.models.jpa;
|
|||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.jpa.entities.FederationProviderEntity;
|
||||
import org.keycloak.models.utils.CredentialValidation;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
|
@ -730,6 +732,64 @@ public class RealmAdapter implements RealmModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FederationProviderModel> getFederationProviders() {
|
||||
List<FederationProviderEntity> entities = realm.getFederationProviders();
|
||||
List<FederationProviderEntity> copy = new ArrayList<FederationProviderEntity>();
|
||||
for (FederationProviderEntity entity : entities) {
|
||||
copy.add(entity);
|
||||
|
||||
}
|
||||
Collections.sort(copy, new Comparator<FederationProviderEntity>() {
|
||||
|
||||
@Override
|
||||
public int compare(FederationProviderEntity o1, FederationProviderEntity o2) {
|
||||
return o1.getPriority() - o2.getPriority();
|
||||
}
|
||||
|
||||
});
|
||||
List<FederationProviderModel> result = new ArrayList<FederationProviderModel>();
|
||||
for (FederationProviderEntity entity : copy) {
|
||||
result.add(new FederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationProviders(List<FederationProviderModel> providers) {
|
||||
List<FederationProviderEntity> newEntities = new ArrayList<FederationProviderEntity>();
|
||||
int counter = 1;
|
||||
for (FederationProviderModel model : providers) {
|
||||
FederationProviderEntity entity = new FederationProviderEntity();
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
entity.setRealm(realm);
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setConfig(model.getConfig());
|
||||
entity.setPriority(counter++);
|
||||
newEntities.add(entity);
|
||||
}
|
||||
|
||||
// Remove all existing first
|
||||
Collection<FederationProviderEntity> existing = realm.getFederationProviders();
|
||||
Collection<FederationProviderEntity> copy = new ArrayList<FederationProviderEntity>(existing);
|
||||
for (FederationProviderEntity apToRemove : copy) {
|
||||
existing.remove(apToRemove);
|
||||
em.remove(apToRemove);
|
||||
}
|
||||
|
||||
em.flush();
|
||||
|
||||
// Now create all new providers
|
||||
for (FederationProviderEntity apToAdd : newEntities) {
|
||||
existing.add(apToAdd);
|
||||
em.persist(apToAdd);
|
||||
}
|
||||
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
|
||||
|
@ -764,7 +824,7 @@ public class RealmAdapter implements RealmModel {
|
|||
return false;
|
||||
}
|
||||
if (!role.getContainer().equals(this)) return false;
|
||||
session.users().preRemove(role);
|
||||
session.users().preRemove(this, role);
|
||||
RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
|
||||
realm.getRoles().remove(role);
|
||||
realm.getDefaultRoles().remove(role);
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.models.jpa;
|
|||
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
|
@ -11,6 +12,7 @@ import org.keycloak.models.UserCredentialValueModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.jpa.entities.CredentialEntity;
|
||||
import org.keycloak.models.jpa.entities.FederationProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.UserAttributeEntity;
|
||||
import org.keycloak.models.jpa.entities.UserEntity;
|
||||
import org.keycloak.models.jpa.entities.UserRequiredActionEntity;
|
||||
|
@ -413,6 +415,16 @@ public class UserAdapter implements UserModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return user.getFederationLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationLink(String link) {
|
||||
user.setFederationLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -1,127 +1,127 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="AUTH_PROVIDER_ENTITY")
|
||||
@IdClass(AuthenticationProviderEntity.Key.class)
|
||||
public class AuthenticationProviderEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "REALM_ID")
|
||||
protected RealmEntity realm;
|
||||
|
||||
@Id
|
||||
@Column(name="PROVIDER_NAME")
|
||||
private String providerName;
|
||||
@Column(name="PASSWORD_UPDATE_SUPPORTED")
|
||||
private boolean passwordUpdateSupported;
|
||||
@Column(name="PRIORITY")
|
||||
private int priority;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="name")
|
||||
@Column(name="value")
|
||||
@CollectionTable(name="AUTH_PROVIDER_CONFIG")
|
||||
private Map<String, String> config;
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(RealmEntity realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public boolean isPasswordUpdateSupported() {
|
||||
return passwordUpdateSupported;
|
||||
}
|
||||
|
||||
public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
|
||||
this.passwordUpdateSupported = passwordUpdateSupported;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected RealmEntity realm;
|
||||
|
||||
protected String providerName;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(RealmEntity realm, String providerName) {
|
||||
this.realm = realm;
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (providerName != null ? !providerName.equals(key.providerName) : key.providerName != null) return false;
|
||||
if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = realm != null ? realm.getId().hashCode() : 0;
|
||||
result = 31 * result + (providerName != null ? providerName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="AUTH_PROVIDER")
|
||||
@IdClass(AuthenticationProviderEntity.Key.class)
|
||||
public class AuthenticationProviderEntity {
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "REALM_ID")
|
||||
protected RealmEntity realm;
|
||||
|
||||
@Id
|
||||
@Column(name="PROVIDER_NAME")
|
||||
private String providerName;
|
||||
@Column(name="PASSWORD_UPDATE_SUPPORTED")
|
||||
private boolean passwordUpdateSupported;
|
||||
@Column(name="PRIORITY")
|
||||
private int priority;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="name")
|
||||
@Column(name="value")
|
||||
@CollectionTable(name="AUTH_PROVIDER_CONFIG")
|
||||
private Map<String, String> config;
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(RealmEntity realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public boolean isPasswordUpdateSupported() {
|
||||
return passwordUpdateSupported;
|
||||
}
|
||||
|
||||
public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
|
||||
this.passwordUpdateSupported = passwordUpdateSupported;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected RealmEntity realm;
|
||||
|
||||
protected String providerName;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(RealmEntity realm, String providerName) {
|
||||
this.realm = realm;
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (providerName != null ? !providerName.equals(key.providerName) : key.providerName != null) return false;
|
||||
if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = realm != null ? realm.getId().hashCode() : 0;
|
||||
result = 31 * result + (providerName != null ? providerName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.models.jpa.entities;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="FEDERATION_PROVIDER")
|
||||
public class FederationProviderEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="ID", length = 36)
|
||||
protected String id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "REALM_ID")
|
||||
protected RealmEntity realm;
|
||||
|
||||
@Column(name="PROVIDER_NAME")
|
||||
private String providerName;
|
||||
@Column(name="PRIORITY")
|
||||
private int priority;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="name")
|
||||
@Column(name="value")
|
||||
@CollectionTable(name="FEDERATION_PROVIDER_CONFIG")
|
||||
private Map<String, String> config;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(RealmEntity realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public void setProviderName(String providerName) {
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -117,6 +117,10 @@ public class RealmEntity {
|
|||
@JoinTable(name="AUTH_PROVIDERS")
|
||||
List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
||||
|
||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
@JoinTable(name="FED_PROVIDERS")
|
||||
List<FederationProviderEntity> federationProviders = new ArrayList<FederationProviderEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
|
||||
|
||||
|
@ -509,5 +513,12 @@ public class RealmEntity {
|
|||
this.masterAdminApp = masterAdminApp;
|
||||
}
|
||||
|
||||
public List<FederationProviderEntity> getFederationProviders() {
|
||||
return federationProviders;
|
||||
}
|
||||
|
||||
public void setFederationProviders(List<FederationProviderEntity> federationProviders) {
|
||||
this.federationProviders = federationProviders;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,9 @@ public class UserEntity {
|
|||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
|
||||
|
||||
@Column(name="federation_link")
|
||||
protected String federationLink;
|
||||
|
||||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
|
||||
protected Collection<AuthenticationLinkEntity> authenticationLink;
|
||||
|
||||
|
@ -198,4 +201,11 @@ public class UserEntity {
|
|||
this.authenticationLink = authenticationLink;
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> im
|
|||
|
||||
@Override
|
||||
public boolean removeRole(RoleModel role) {
|
||||
session.users().preRemove(role);
|
||||
session.users().preRemove(getRealm(), role);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ public class MongoUserProvider implements UserProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preRemove(RoleModel role) {
|
||||
public void preRemove(RealmModel realm, RoleModel role) {
|
||||
// todo not sure what to do for this
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
|||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.FederationProviderModel;
|
||||
import org.keycloak.models.entities.FederationProviderEntity;
|
||||
import org.keycloak.models.utils.CredentialValidation;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
|
@ -475,7 +477,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
public boolean removeRoleById(String id) {
|
||||
RoleModel role = getRoleById(id);
|
||||
if (role == null) return false;
|
||||
session.users().preRemove(role);
|
||||
session.users().preRemove(this, role);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
|
@ -793,6 +795,31 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
realm.setAuthenticationProviders(entities);
|
||||
updateRealm();
|
||||
}
|
||||
@Override
|
||||
public List<FederationProviderModel> getFederationProviders() {
|
||||
List<FederationProviderEntity> entities = realm.getFederationProviders();
|
||||
List<FederationProviderModel> result = new ArrayList<FederationProviderModel>();
|
||||
for (FederationProviderEntity entity : entities) {
|
||||
result.add(new FederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationProviders(List<FederationProviderModel> providers) {
|
||||
List<FederationProviderEntity> entities = new ArrayList<FederationProviderEntity>();
|
||||
for (FederationProviderModel model : providers) {
|
||||
FederationProviderEntity entity = new FederationProviderEntity();
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
entity.setProviderName(model.getProviderName());
|
||||
entity.setConfig(model.getConfig());
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
realm.setFederationProviders(entities);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuditEnabled() {
|
||||
|
|
|
@ -330,6 +330,17 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return user.getFederationLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFederationLink(String link) {
|
||||
user.setFederationLink(link);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationLinkModel getAuthenticationLink() {
|
||||
AuthenticationLinkEntity authLinkEntity = user.getAuthenticationLink();
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -103,6 +103,7 @@
|
|||
<module>model</module>
|
||||
<module>integration</module>
|
||||
<module>picketlink</module>
|
||||
<module>federation</module>
|
||||
<module>services</module>
|
||||
<module>social</module>
|
||||
<module>forms</module>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.services;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakTransactionManager;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.UserProvider;
|
||||
|
@ -32,7 +33,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
this.transactionManager = new DefaultKeycloakTransactionManager();
|
||||
}
|
||||
|
||||
private RealmProvider getModelProvider() {
|
||||
private RealmProvider getRealmProvider() {
|
||||
if (factory.getDefaultProvider(CacheRealmProvider.class) != null) {
|
||||
return getProvider(CacheRealmProvider.class);
|
||||
} else {
|
||||
|
@ -53,6 +54,16 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
return transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSessionFactory getKeycloakSessionFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserProvider userStorage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends Provider> T getProvider(Class<T> clazz) {
|
||||
Integer hash = clazz.hashCode();
|
||||
T provider = (T) providers.get(hash);
|
||||
|
@ -95,7 +106,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
@Override
|
||||
public RealmProvider realms() {
|
||||
if (model == null) {
|
||||
model = getModelProvider();
|
||||
model = getRealmProvider();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
|
|
@ -74,11 +74,13 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
|
|||
return provider.get(clazz);
|
||||
}
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
|
||||
@Override
|
||||
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
|
||||
return getProviderFactory(clazz, provider.get(clazz));
|
||||
}
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
|
||||
@Override
|
||||
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
|
||||
return factoriesMap.get(clazz).get(id);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue