keycloak-aplcache
Changes
broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java 2(+2 -0)
broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java 8(+7 -1)
broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper 3(+2 -1)
broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper 3(+2 -1)
Details
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
index f4ddeae..e402e04 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
@@ -74,6 +74,11 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
}
@Override
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context) {
+
+ }
+
+ @Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
index 28286ee..898cee4 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
@@ -18,8 +18,10 @@
package org.keycloak.broker.provider;
import org.keycloak.Config;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
import java.io.InputStream;
import java.util.HashMap;
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
index 15b83e8..092f23b 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
@@ -1,8 +1,10 @@
package org.keycloak.broker.provider;
import org.keycloak.broker.provider.IdentityProviderMapper;
+import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -28,4 +30,9 @@ public abstract class AbstractIdentityProviderMapper implements IdentityProvider
public void postInit(KeycloakSessionFactory factory) {
}
+
+ @Override
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+
+ }
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java b/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
index 7bee936..f90f803 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
@@ -32,6 +32,7 @@ public class BrokeredIdentityContext {
private String id;
private String username;
+ private String modelUsername;
private String email;
private String firstName;
private String lastName;
@@ -59,6 +60,11 @@ public class BrokeredIdentityContext {
this.id = id;
}
+ /**
+ * Username in remote idp
+ *
+ * @return
+ */
public String getUsername() {
return username;
}
@@ -67,6 +73,19 @@ public class BrokeredIdentityContext {
this.username = username;
}
+ /**
+ * username to store in UserModel
+ *
+ * @return
+ */
+ public String getModelUsername() {
+ return modelUsername;
+ }
+
+ public void setModelUsername(String modelUsername) {
+ this.modelUsername = modelUsername;
+ }
+
public String getEmail() {
return email;
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
index 2c503bb..47037fa 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -20,6 +20,7 @@ package org.keycloak.broker.provider;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -46,6 +47,8 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
public Response authenticated(BrokeredIdentityContext context);
}
+
+ void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context);
void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
old mode 100644
new mode 100755
index 1f1bdcc..db2570d
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
@@ -18,6 +18,8 @@
package org.keycloak.broker.provider;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import java.io.InputStream;
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
index 4017499..d20aa00 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
@@ -19,7 +19,37 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
String getDisplayCategory();
String getDisplayType();
+ /**
+ * Called to determine what keycloak username and email to use to process the login request from the external IDP
+ * Usually used to map BrokeredIdentityContet.username or email.
+ *
+ * @param session
+ * @param realm
+ * @param mapperModel
+ * @param context
+ */
+ void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
+
+ /**
+ * Called after UserModel is created for first time for this user.
+ *
+ * @param session
+ * @param realm
+ * @param user
+ * @param mapperModel
+ * @param context
+ */
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
+
+ /**
+ * Called when this user has logged in before and has already been imported.
+ *
+ * @param session
+ * @param realm
+ * @param user
+ * @param mapperModel
+ * @param context
+ */
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
index b59a427..8d9f44a 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
@@ -17,10 +17,15 @@
*/
package org.keycloak.broker.oidc;
+import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
import java.io.InputStream;
+import java.util.HashMap;
import java.util.Map;
/**
@@ -50,4 +55,5 @@ public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProvide
return OIDCIdentityProviderFactory.parseOIDCConfig(inputStream);
}
-}
+
+ }
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
index f7ebf94..85ec561 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimMapper.java
@@ -33,8 +33,12 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
return null;
}
- protected Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public static Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String claim = mapperModel.getConfig().get(CLAIM);
+ return getClaimValue(context, claim);
+ }
+
+ public static Object getClaimValue(BrokeredIdentityContext context, String claim) {
{ // search access token
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
if (token != null) {
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java
new file mode 100755
index 0000000..b648f5f
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java
@@ -0,0 +1,109 @@
+package org.keycloak.broker.oidc.mappers;
+
+import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
+import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.models.IdentityProviderMapperModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UsernameTemplateMapper extends AbstractClaimMapper {
+
+ public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
+
+ private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+ public static final String TEMPLATE = "template";
+
+ static {
+ ProviderConfigProperty property;
+ property = new ProviderConfigProperty();
+ property.setName(TEMPLATE);
+ property.setLabel("Template");
+ property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${CLAIM.sub}'. ALIAS is the provider alias. CLAIM.<NAME> references an ID or Access token claim.");
+ property.setType(ProviderConfigProperty.STRING_TYPE);
+ property.setDefaultValue("${ALIAS}.${CLAIM.preferred_username}");
+ configProperties.add(property);
+ }
+
+ public static final String PROVIDER_ID = "oidc-username-idp-mapper";
+
+ @Override
+ public List<ProviderConfigProperty> getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public String[] getCompatibleProviders() {
+ return COMPATIBLE_PROVIDERS;
+ }
+
+ @Override
+ public String getDisplayCategory() {
+ return "Preprocessor";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Username Template Importer";
+ }
+
+ @Override
+ public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ }
+
+ @Override
+ public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ }
+
+ static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
+
+ @Override
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ String template = mapperModel.getConfig().get(TEMPLATE);
+ Matcher m = substitution.matcher(template);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String variable = m.group(1);
+ if (variable.equals("ALIAS")) {
+ m.appendReplacement(sb, context.getIdpConfig().getAlias());
+ } else if (variable.equals("UUID")) {
+ m.appendReplacement(sb, KeycloakModelUtils.generateId());
+ } else if (variable.startsWith("CLAIM.")) {
+ String name = variable.substring("CLAIM.".length());
+ Object value = AbstractClaimMapper.getClaimValue(context, name);
+ if (value == null) value = "";
+ m.appendReplacement(sb, value.toString());
+ } else {
+ m.appendReplacement(sb, m.group(1));
+ }
+
+ }
+ m.appendTail(sb);
+ String username = sb.toString();
+ context.setModelUsername(username);
+
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Format the username to import.";
+ }
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
index ca0c1bb..c959aec 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -17,11 +17,15 @@
*/
package org.keycloak.broker.oidc;
+import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKParser;
+import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
@@ -30,6 +34,7 @@ import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.InputStream;
import java.security.PublicKey;
+import java.util.HashMap;
import java.util.Map;
/**
@@ -89,7 +94,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
}
} catch (IOException e) {
- throw new RuntimeException("F ailed to query JWKSet from: " + uri, e);
+ throw new RuntimeException("Failed to query JWKSet from: " + uri, e);
}
}
@@ -99,4 +104,5 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
protected static boolean keyTypeSupported(String type) {
return type != null && type.equals("RSA");
}
+
}
diff --git a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
index de86af2..62e6741 100755
--- a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
+++ b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
@@ -1,3 +1,4 @@
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
-org.keycloak.broker.oidc.mappers.UserAttributeMapper
\ No newline at end of file
+org.keycloak.broker.oidc.mappers.UserAttributeMapper
+org.keycloak.broker.oidc.mappers.UsernameTemplateMapper
\ No newline at end of file
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java
new file mode 100755
index 0000000..0ed0c1b
--- /dev/null
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java
@@ -0,0 +1,133 @@
+package org.keycloak.broker.saml.mappers;
+
+import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.broker.saml.SAMLEndpoint;
+import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
+import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.dom.saml.v2.assertion.AttributeType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.assertion.SubjectType;
+import org.keycloak.models.IdentityProviderMapperModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
+
+ public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
+
+ private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+ public static final String TEMPLATE = "template";
+
+ static {
+ ProviderConfigProperty property;
+ property = new ProviderConfigProperty();
+ property.setName(TEMPLATE);
+ property.setLabel("Template");
+ property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${NAMEID}'. ALIAS is the provider alias. NAMEID is that SAML name id assertion. ATTRIBUTE.<NAME> references a SAML attribute where name is the attribute name or friendly name.");
+ property.setType(ProviderConfigProperty.STRING_TYPE);
+ property.setDefaultValue("${ALIAS}.${NAMEID}");
+ configProperties.add(property);
+ }
+
+ public static final String PROVIDER_ID = "saml-username-idp-mapper";
+
+ @Override
+ public List<ProviderConfigProperty> getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public String[] getCompatibleProviders() {
+ return COMPATIBLE_PROVIDERS;
+ }
+
+ @Override
+ public String getDisplayCategory() {
+ return "Preprocessor";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Username Template Importer";
+ }
+
+ @Override
+ public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+
+ }
+
+ @Override
+ public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+
+ }
+ static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
+
+ @Override
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
+ String template = mapperModel.getConfig().get(TEMPLATE);
+ Matcher m = substitution.matcher(template);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String variable = m.group(1);
+ if (variable.equals("ALIAS")) {
+ m.appendReplacement(sb, context.getIdpConfig().getAlias());
+ } else if (variable.equals("UUID")) {
+ m.appendReplacement(sb, KeycloakModelUtils.generateId());
+ } else if (variable.equals("NAMEID")) {
+ SubjectType subject = assertion.getSubject();
+ SubjectType.STSubType subType = subject.getSubType();
+ NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+ m.appendReplacement(sb, subjectNameID.getValue());
+ } else if (variable.startsWith("ATTRIBUTE.")) {
+ String name = variable.substring("ATTRIBUTE.".length());
+ String value = "";
+ for (AttributeStatementType statement : assertion.getAttributeStatements()) {
+ for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
+ AttributeType attr = choice.getAttribute();
+ if (name.equals(attr.getName()) || name.equals(attr.getFriendlyName())) {
+ List<Object> attributeValue = attr.getAttributeValue();
+ if (attributeValue != null && !attributeValue.isEmpty()) {
+ value = attributeValue.get(0).toString();
+ }
+ break;
+ }
+ }
+ }
+ m.appendReplacement(sb, value);
+ } else {
+ m.appendReplacement(sb, m.group(1));
+ }
+
+ }
+ m.appendTail(sb);
+ context.setModelUsername(sb.toString());
+
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Format the username to import.";
+ }
+
+}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
index 98ad908..1cd28fd 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
@@ -18,13 +18,17 @@
package org.keycloak.broker.saml;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.broker.saml.mappers.UsernameTemplateMapper;
import org.keycloak.dom.saml.v2.metadata.EndpointType;
import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
+import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.DocumentUtil;
@@ -150,4 +154,5 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
public String getId() {
return PROVIDER_ID;
}
+
}
diff --git a/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
index 9de7273..89cdf92 100755
--- a/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
+++ b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
@@ -1,2 +1,3 @@
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
-org.keycloak.broker.saml.mappers.UserAttributeMapper
\ No newline at end of file
+org.keycloak.broker.saml.mappers.UserAttributeMapper
+org.keycloak.broker.saml.mappers.UsernameTemplateMapper
\ No newline at end of file
diff --git a/core/src/test/java/org/keycloak/JsonParserTest.java b/core/src/test/java/org/keycloak/JsonParserTest.java
index 010eae4..ed0831d 100755
--- a/core/src/test/java/org/keycloak/JsonParserTest.java
+++ b/core/src/test/java/org/keycloak/JsonParserTest.java
@@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.jackson.annotate.JsonAnyGetter;
@@ -90,4 +91,21 @@ public class JsonParserTest {
Assert.assertEquals(100, config.getCorsMaxAge());
Assert.assertEquals(200, config.getConnectionPoolSize());
}
+
+ static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
+
+ @Test
+ public void testSub() {
+ String pattern = "${ALIAS}.${CRAP}";
+ Matcher m = substitution.matcher(pattern);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ System.out.println("GROUP: " + m.group(1));
+ m.appendReplacement(sb, m.group(1));
+
+ }
+ m.appendTail(sb);
+ System.out.println(sb.toString());
+ }
+
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
index 5264f17..d812a34 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -186,4 +186,21 @@ public class UserEntity {
public void setFederationLink(String federationLink) {
this.federationLink = federationLink;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UserEntity that = (UserEntity) o;
+
+ if (!id.equals(that.id)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index d0af512..dee433e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -86,12 +86,21 @@ public class JpaUserProvider implements UserProvider {
}
private void removeUser(UserEntity user) {
+ String id = user.getId();
em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteFederatedIdentityByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteUserConsentRolesByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteUserConsentProtMappersByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteUserConsentsByUser").setParameter("user", user).executeUpdate();
- em.remove(user);
+ em.flush();
+ // not sure why i have to do a clear() here. I was getting some messed up errors that Hibernate couldn't
+ // un-delete the UserEntity.
+ em.clear();
+ user = em.find(UserEntity.class, id);
+ if (user != null) {
+ em.remove(user);
+ }
+ em.flush();
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/managers/UserManager.java b/services/src/main/java/org/keycloak/services/managers/UserManager.java
index c6732b7..3c0252c 100755
--- a/services/src/main/java/org/keycloak/services/managers/UserManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserManager.java
@@ -17,11 +17,11 @@ public class UserManager {
}
public boolean removeUser(RealmModel realm, UserModel user) {
+ UserSessionProvider sessions = session.sessions();
+ if (sessions != null) {
+ sessions.onUserRemoved(realm, user);
+ }
if (session.users().removeUser(realm, user)) {
- UserSessionProvider sessions = session.sessions();
- if (sessions != null) {
- sessions.onUserRemoved(realm, user);
- }
return true;
}
return false;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index ec33725..ecedae7 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -129,7 +129,8 @@ public class IdentityProvidersResource {
this.auth.requireManage();
try {
- this.realm.addIdentityProvider(RepresentationToModel.toModel(representation));
+ IdentityProviderModel identityProvider = RepresentationToModel.toModel(representation);
+ this.realm.addIdentityProvider(identityProvider);
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
} catch (ModelDuplicateException e) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index a8119ce..e6660ee 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -44,6 +44,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.AccessToken;
@@ -249,6 +250,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
ClientSessionModel clientSession = clientCode.getClientSession();
+ context.getIdp().preprocessFederatedIdentity(session, realmModel, context);
+ Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(context.getIdpConfig().getAlias());
+ if (mappers != null) {
+ KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+ for (IdentityProviderMapperModel mapper : mappers) {
+ IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
+ target.preprocessFederatedIdentity(session, realmModel, mapper, context);
+ }
+ }
+
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, context.getId(),
context.getUsername(), context.getToken());
@@ -492,18 +503,24 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
fireErrorEvent(Errors.FEDERATED_IDENTITY_EMAIL_EXISTS);
throw new IdentityBrokerException(Messages.FEDERATED_IDENTITY_EMAIL_EXISTS);
}
-
- String username = context.getUsername();
- if (this.realmModel.isRegistrationEmailAsUsername() && !Validation.isEmpty(context.getEmail())) {
- username = context.getEmail();
- } else if (username == null) {
- username = context.getIdpConfig().getAlias() + "." + context.getId();
- } else {
- username = context.getIdpConfig().getAlias() + "." + context.getUsername();
+ String username = context.getModelUsername();
+ if (username == null) {
+ username = context.getUsername();
+ if (this.realmModel.isRegistrationEmailAsUsername() && !Validation.isEmpty(context.getEmail())) {
+ username = context.getEmail();
+ } else if (username == null) {
+ username = context.getIdpConfig().getAlias() + "." + context.getId();
+ } else {
+ username = context.getIdpConfig().getAlias() + "." + context.getUsername();
+ }
}
- if (username != null) {
- username = username.trim();
+ if (username == null) {
+ LOGGER.warn("Unknown username");
+ fireErrorEvent(Errors.FEDERATED_IDENTITY_USERNAME_EXISTS);
+ throw new IdentityBrokerException(Messages.FEDERATED_IDENTITY_USERNAME_EXISTS);
+
}
+ username = username.trim();
existingUser = this.session.users().getUserByUsername(username, this.realmModel);
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
index d9db0e9..40a883b 100755
--- a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -33,7 +33,8 @@ public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider imple
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
- user.setUsername(getJsonProperty(profile, "login"));
+ String username = getJsonProperty(profile, "login");
+ user.setUsername(username);
user.setName(getJsonProperty(profile, "name"));
user.setEmail(getJsonProperty(profile, "email"));
user.setIdpConfig(getConfig());
diff --git a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
index c031443..5c5265e 100755
--- a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
+++ b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
@@ -60,7 +60,8 @@ public class LinkedInIdentityProvider extends AbstractOAuth2IdentityProvider imp
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
- user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl")));
+ String username = extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl"));
+ user.setUsername(username);
user.setName(getJsonProperty(profile, "formattedName"));
user.setEmail(getJsonProperty(profile, "emailAddress"));
user.setIdpConfig(getConfig());
diff --git a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
index 3e9cc8c..280753b 100755
--- a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
+++ b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
@@ -69,7 +69,8 @@ public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvide
BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "user_id"));
- user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "link")));
+ String username = extractUsernameFromProfileURL(getJsonProperty(profile, "link"));
+ user.setUsername(username);
user.setName(unescapeHtml3(getJsonProperty(profile, "display_name")));
// email is not provided
// user.setEmail(getJsonProperty(profile, "email"));