diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
index 0de32d8650..71fc2c4be4 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -5,14 +5,24 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
@@ -80,11 +90,13 @@
+
+
diff --git a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
index e7219410de..4f132e106a 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
@@ -1,17 +1,21 @@
package org.keycloak.representations.idm;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* @author Bill Burke
* @version $Revision: 1 $
*/
public class ProtocolMapperRepresentation {
protected String id;
- protected String protocolClaim;
+ protected String name;
protected String protocol;
- protected String source;
- protected String sourceAttribute;
protected String protocolMapper;
protected boolean appliedByDefault;
+ protected boolean consentRequired;
+ protected String consentText;
+ protected Map config = new HashMap();
public String getId() {
@@ -22,12 +26,12 @@ public class ProtocolMapperRepresentation {
this.id = id;
}
- public String getProtocolClaim() {
- return protocolClaim;
+ public String getName() {
+ return name;
}
- public void setProtocolClaim(String protocolClaim) {
- this.protocolClaim = protocolClaim;
+ public void setName(String name) {
+ this.name = name;
}
public String getProtocol() {
@@ -38,14 +42,6 @@ public class ProtocolMapperRepresentation {
this.protocol = protocol;
}
- public String getSourceAttribute() {
- return sourceAttribute;
- }
-
- public void setSourceAttribute(String sourceAttribute) {
- this.sourceAttribute = sourceAttribute;
- }
-
public boolean isAppliedByDefault() {
return appliedByDefault;
}
@@ -54,14 +50,6 @@ public class ProtocolMapperRepresentation {
this.appliedByDefault = appliedByDefault;
}
- public String getSource() {
- return source;
- }
-
- public void setSource(String source) {
- this.source = source;
- }
-
public String getProtocolMapper() {
return protocolMapper;
}
@@ -69,4 +57,28 @@ public class ProtocolMapperRepresentation {
public void setProtocolMapper(String protocolMapper) {
this.protocolMapper = protocolMapper;
}
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+
+ public boolean isConsentRequired() {
+ return consentRequired;
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ this.consentRequired = consentRequired;
+ }
+
+ public String getConsentText() {
+ return consentText;
+ }
+
+ public void setConsentText(String consentText) {
+ this.consentText = consentText;
+ }
}
diff --git a/docbook/reference/en/en-US/modules/adapter-config.xml b/docbook/reference/en/en-US/modules/adapter-config.xml
index d4ef6423c3..21cc77a27c 100755
--- a/docbook/reference/en/en-US/modules/adapter-config.xml
+++ b/docbook/reference/en/en-US/modules/adapter-config.xml
@@ -14,7 +14,7 @@
"use-resource-role-mappings" : false,
"enable-cors" : true,
"cors-max-age" : 1000,
- "cors-allowed-methods" : [ "POST", "PUT", "DELETE", "GET" ],
+ "cors-allowed-methods" : "POST, PUT, DELETE, GET",
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
diff --git a/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java b/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
index 001bb9600f..f27a9ea5ee 100755
--- a/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
@@ -1,5 +1,7 @@
package org.keycloak.models;
+import java.util.Map;
+
/**
* Specifies a mapping from user data to a protocol claim assertion. If protocolMapper is set, this points
* to a @Provider that will perform the mapping. If you have this set, then no other attributes of this class need to be set.
@@ -10,21 +12,14 @@ package org.keycloak.models;
* @version $Revision: 1 $
*/
public class ProtocolMapperModel {
- public static enum Source {
- USER_MODEL,
- USER_ATTRIBUTE,
- USER_SESSION_NOTE,
- CLIENT_SESSION_NOTE
- }
-
protected String id;
protected String name;
- protected String protocolClaim;
protected String protocol;
- protected Source source;
- protected String sourceAttribute;
protected String protocolMapper;
+ protected boolean consentRequired;
+ protected String consentText;
protected boolean appliedByDefault;
+ protected Map config;
public String getId() {
@@ -43,14 +38,6 @@ public class ProtocolMapperModel {
this.name = name;
}
- public String getProtocolClaim() {
- return protocolClaim;
- }
-
- public void setProtocolClaim(String protocolClaim) {
- this.protocolClaim = protocolClaim;
- }
-
public String getProtocol() {
return protocol;
}
@@ -59,14 +46,6 @@ public class ProtocolMapperModel {
this.protocol = protocol;
}
- public String getSourceAttribute() {
- return sourceAttribute;
- }
-
- public void setSourceAttribute(String sourceAttribute) {
- this.sourceAttribute = sourceAttribute;
- }
-
public boolean isAppliedByDefault() {
return appliedByDefault;
}
@@ -75,14 +54,6 @@ public class ProtocolMapperModel {
this.appliedByDefault = appliedByDefault;
}
- public Source getSource() {
- return source;
- }
-
- public void setSource(Source source) {
- this.source = source;
- }
-
public String getProtocolMapper() {
return protocolMapper;
}
@@ -91,6 +62,30 @@ public class ProtocolMapperModel {
this.protocolMapper = protocolMapper;
}
+ public boolean isConsentRequired() {
+ return consentRequired;
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ this.consentRequired = consentRequired;
+ }
+
+ public String getConsentText() {
+ return consentText;
+ }
+
+ public void setConsentText(String consentText) {
+ this.consentText = consentText;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/api/src/main/java/org/keycloak/models/RealmListenerHelper.java b/model/api/src/main/java/org/keycloak/models/RealmListenerHelper.java
new file mode 100755
index 0000000000..b7c074dd92
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/RealmListenerHelper.java
@@ -0,0 +1,31 @@
+package org.keycloak.models;
+
+import java.util.LinkedList;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RealmListenerHelper {
+ protected LinkedList list = new LinkedList();
+
+ public void registerListener(RealmProvider.RealmCreationListener listener) {
+ synchronized (list) {
+ list.add(listener);
+ }
+ }
+
+ public void unregisterListener(RealmProvider.RealmCreationListener listener) {
+ synchronized (list) {
+ list.remove(listener);
+ }
+ }
+
+ public void executeCreationListeners(RealmModel realm) {
+ synchronized (list) {
+ for (RealmProvider.RealmCreationListener listener : list) {
+ listener.created(realm);
+ }
+ }
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/RealmProvider.java b/model/api/src/main/java/org/keycloak/models/RealmProvider.java
index 4ec3c87591..2c04cc1627 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmProvider.java
@@ -9,10 +9,16 @@ import java.util.List;
* @version $Revision: 1 $
*/
public interface RealmProvider extends Provider {
+ public interface RealmCreationListener {
+ void created(RealmModel realm);
+ }
+
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
RealmModel createRealm(String name);
RealmModel createRealm(String id, String name);
+ void registerListener(RealmCreationListener listener);
+ void unregisterListener(RealmCreationListener listener);
RealmModel getRealm(String id);
RealmModel getRealmByName(String name);
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ProtocolMapperEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ProtocolMapperEntity.java
index de8f793eec..13f4cfc333 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ProtocolMapperEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ProtocolMapperEntity.java
@@ -2,18 +2,21 @@ package org.keycloak.models.entities;
import org.keycloak.models.ProtocolMapperModel;
+import java.util.Map;
+
/**
* @author Bill Burke
* @version $Revision: 1 $
*/
public class ProtocolMapperEntity {
protected String id;
- protected String protocolClaim;
+ protected String name;
protected String protocol;
- protected ProtocolMapperModel.Source source;
- protected String sourceAttribute;
protected String protocolMapper;
protected boolean appliedByDefault;
+ protected boolean consentRequired;
+ protected String consentText;
+ protected Map config;
public String getId() {
return id;
@@ -23,12 +26,12 @@ public class ProtocolMapperEntity {
this.id = id;
}
- public String getProtocolClaim() {
- return protocolClaim;
+ public String getName() {
+ return name;
}
- public void setProtocolClaim(String protocolClaim) {
- this.protocolClaim = protocolClaim;
+ public void setName(String name) {
+ this.name = name;
}
public String getProtocol() {
@@ -39,22 +42,6 @@ public class ProtocolMapperEntity {
this.protocol = protocol;
}
- public ProtocolMapperModel.Source getSource() {
- return source;
- }
-
- public void setSource(ProtocolMapperModel.Source source) {
- this.source = source;
- }
-
- public String getSourceAttribute() {
- return sourceAttribute;
- }
-
- public void setSourceAttribute(String sourceAttribute) {
- this.sourceAttribute = sourceAttribute;
- }
-
public boolean isAppliedByDefault() {
return appliedByDefault;
}
@@ -70,4 +57,28 @@ public class ProtocolMapperEntity {
public void setProtocolMapper(String protocolMapper) {
this.protocolMapper = protocolMapper;
}
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+
+ public boolean isConsentRequired() {
+ return consentRequired;
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ this.consentRequired = consentRequired;
+ }
+
+ public String getConsentText() {
+ return consentText;
+ }
+
+ public void setConsentText(String consentText) {
+ this.consentText = consentText;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 7db0bac78f..b511a32896 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -53,7 +53,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List userFederationProviders = new ArrayList();
private List identityProviders = new ArrayList();
private List claimTypes = new ArrayList();
- private List claimMappings = new ArrayList();
+ private List protocolMappers = new ArrayList();
private Map browserSecurityHeaders = new HashMap();
private Map smtpConfig = new HashMap();
@@ -401,12 +401,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.claimTypes = claimTypes;
}
- public List getClaimMappings() {
- return claimMappings;
+ public List getProtocolMappers() {
+ return protocolMappers;
}
- public void setClaimMappings(List claimMappings) {
- this.claimMappings = claimMappings;
+ public void setProtocolMappers(List protocolMappers) {
+ this.protocolMappers = protocolMappers;
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 5d0c582615..69d5477f07 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -341,11 +341,14 @@ public class ModelToRepresentation {
ProtocolMapperRepresentation rep = new ProtocolMapperRepresentation();
rep.setId(model.getId());
rep.setProtocol(model.getProtocol());
- rep.setProtocolClaim(model.getProtocolClaim());
- rep.setSourceAttribute(model.getSourceAttribute());
- rep.setSource(model.getSource().name());
+ Map config = new HashMap();
+ config.putAll(model.getConfig());
+ rep.setConfig(config);
+ rep.setName(model.getName());
rep.setProtocolMapper(model.getProtocolMapper());
rep.setAppliedByDefault(model.isAppliedByDefault());
+ rep.setConsentText(model.getConsentText());
+ rep.setConsentRequired(model.isConsentRequired());
return rep;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 6c41613797..2fb49af311 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -118,7 +118,7 @@ public class RepresentationToModel {
importIdentityProviders(rep, newRealm);
importClaimTypes(rep, newRealm);
- importProtocolClaimMappings(rep, newRealm);
+ importProtocolMappers(rep, newRealm);
if (rep.getApplications() != null) {
Map appMap = createApplications(rep, newRealm);
@@ -772,10 +772,15 @@ public class RepresentationToModel {
}
}
- private static void importProtocolClaimMappings(RealmRepresentation rep, RealmModel newRealm) {
+ private static void importProtocolMappers(RealmRepresentation rep, RealmModel newRealm) {
if (rep.getProtocolClaimMappings() != null) {
+ // we make sure we don't recreate mappers that are automatically created by the protocol providers.
for (ProtocolMapperRepresentation representation : rep.getProtocolClaimMappings()) {
- newRealm.addProtocolMapper(toModel(representation));
+ if (representation.getId() == null || newRealm.getProtocolMapperById(representation.getId()) == null) {
+ newRealm.addProtocolMapper(toModel(representation));
+ } else {
+ newRealm.updateProtocolMapper(toModel(representation));
+ }
}
}
}
@@ -808,12 +813,13 @@ public class RepresentationToModel {
public static ProtocolMapperModel toModel(ProtocolMapperRepresentation rep) {
ProtocolMapperModel model = new ProtocolMapperModel();
model.setId(rep.getId());
+ model.setName(rep.getName());
model.setAppliedByDefault(rep.isAppliedByDefault());
- model.setSource(ProtocolMapperModel.Source.valueOf(rep.getSource()));
- model.setSourceAttribute(rep.getSourceAttribute());
+ model.setConsentRequired(rep.isConsentRequired());
+ model.setConsentText(rep.getConsentText());
model.setProtocol(rep.getProtocol());
- model.setProtocolClaim(rep.getProtocolClaim());
model.setProtocolMapper(rep.getProtocolMapper());
+ model.setConfig(rep.getConfig());
return model;
}
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
index a1d232ecb3..9419c91ee2 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
@@ -50,6 +50,17 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
session.getTransaction().enlistAfterCompletion(getTransaction());
}
+ @Override
+ public void registerListener(RealmCreationListener listener) {
+ getDelegate().registerListener(listener);
+ }
+
+ @Override
+ public void unregisterListener(RealmCreationListener listener) {
+ getDelegate().unregisterListener(listener);
+
+ }
+
@Override
public boolean isEnabled() {
return cache.isEnabled();
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
index e7b1551c2d..9896e51281 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheRealmProvider.java
@@ -38,6 +38,16 @@ public class NoCacheRealmProvider implements CacheRealmProvider {
return delegate;
}
+ @Override
+ public void registerListener(RealmCreationListener listener) {
+ getDelegate().registerListener(listener);
+ }
+
+ @Override
+ public void unregisterListener(RealmCreationListener listener) {
+ getDelegate().unregisterListener(listener);
+
+ }
@Override
public void registerRealmInvalidation(String id) {
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 9faace4dde..c06cdecf57 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -362,11 +362,16 @@ public abstract class ClientAdapter implements ClientModel {
for (ProtocolMapperEntity entity : this.entity.getProtocolMappers()) {
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
+ mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
- mapping.setProtocolClaim(entity.getProtocolClaim());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(ProtocolMapperModel.Source.valueOf(entity.getSource()));
- mapping.setSourceAttribute(entity.getSourceAttribute());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) {
+ config.putAll(entity.getConfig());
+ }
+ mapping.setConfig(config);
mappings.add(mapping);
}
return mappings;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 755fb6bff9..5529b83749 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -3,6 +3,7 @@ package org.keycloak.models.jpa;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmListenerHelper;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
@@ -25,10 +26,24 @@ import java.util.List;
public class JpaRealmProvider implements RealmProvider {
private final KeycloakSession session;
protected EntityManager em;
+ protected RealmListenerHelper listeners;
- public JpaRealmProvider(KeycloakSession session, EntityManager em) {
+
+ public JpaRealmProvider(KeycloakSession session, EntityManager em, RealmListenerHelper listeners) {
this.session = session;
this.em = em;
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void registerListener(RealmCreationListener listener) {
+ listeners.registerListener(listener);
+ }
+
+ @Override
+ public void unregisterListener(RealmCreationListener listener) {
+ listeners.unregisterListener(listener);
+
}
@Override
@@ -43,7 +58,9 @@ public class JpaRealmProvider implements RealmProvider {
realm.setId(id);
em.persist(realm);
em.flush();
- return new RealmAdapter(session, em, realm);
+ RealmModel model = new RealmAdapter(session, em, realm);
+ listeners.executeCreationListeners(model);
+ return model;
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java
index 7e090c2142..4869ca745c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProviderFactory.java
@@ -3,6 +3,7 @@ package org.keycloak.models.jpa;
import org.keycloak.Config;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmListenerHelper;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmProviderFactory;
@@ -14,6 +15,8 @@ import javax.persistence.EntityManager;
*/
public class JpaRealmProviderFactory implements RealmProviderFactory {
+ protected RealmListenerHelper listeners = new RealmListenerHelper();
+
@Override
public void init(Config.Scope config) {
}
@@ -26,7 +29,7 @@ public class JpaRealmProviderFactory implements RealmProviderFactory {
@Override
public RealmProvider create(KeycloakSession session) {
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
- return new JpaRealmProvider(session, em);
+ return new JpaRealmProvider(session, em, listeners);
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index f294bbde4b..7578fd0ae0 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1262,12 +1262,17 @@ public class RealmAdapter implements RealmModel {
for (ProtocolMapperEntity entity : realm.getProtocolClaimMappings()) {
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
+ mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
- mapping.setProtocolClaim(entity.getProtocolClaim());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(ProtocolMapperModel.Source.valueOf(entity.getSource()));
- mapping.setSourceAttribute(entity.getSourceAttribute());
mapping.setProtocolMapper(entity.getProtocolMapper());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) {
+ config.putAll(entity.getConfig());
+ }
+ mapping.setConfig(config);
mappings.add(mapping);
}
return mappings;
@@ -1278,26 +1283,29 @@ public class RealmAdapter implements RealmModel {
String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId();
ProtocolMapperEntity entity = new ProtocolMapperEntity();
entity.setId(id);
- entity.setSourceAttribute(model.getSourceAttribute());
+ entity.setName(model.getName());
entity.setProtocol(model.getProtocol());
- entity.setProtocolClaim(model.getProtocolClaim());
- entity.setSource(model.getSource().name());
entity.setProtocolMapper(model.getProtocolMapper());
entity.setAppliedByDefault(model.isAppliedByDefault());
entity.setRealm(realm);
+ entity.setConfig(model.getConfig());
+ entity.setConsentRequired(model.isConsentRequired());
+ entity.setConsentText(model.getConsentText());
+
em.persist(entity);
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
+ mapping.setName(model.getName());
mapping.setProtocol(entity.getProtocol());
mapping.setProtocolMapper(entity.getProtocolMapper());
- mapping.setProtocolClaim(entity.getProtocolClaim());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(ProtocolMapperModel.Source.valueOf(entity.getSource()));
- mapping.setSourceAttribute(entity.getSourceAttribute());
+ mapping.setConfig(model.getConfig());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
return mapping;
}
- protected ProtocolMapperEntity getProtocolClaimMapping(String id) {
+ protected ProtocolMapperEntity getProtocolMapper(String id) {
for (ProtocolMapperEntity entity : realm.getProtocolClaimMappings()) {
if (entity.getId().equals(id)) {
return entity;
@@ -1309,7 +1317,7 @@ public class RealmAdapter implements RealmModel {
@Override
public void removeProtocolMapper(ProtocolMapperModel mapping) {
- ProtocolMapperEntity toDelete = getProtocolClaimMapping(mapping.getId());
+ ProtocolMapperEntity toDelete = getProtocolMapper(mapping.getId());
if (toDelete != null) {
realm.getProtocolClaimMappings().remove(toDelete);
em.remove(toDelete);
@@ -1319,27 +1327,35 @@ public class RealmAdapter implements RealmModel {
@Override
public void updateProtocolMapper(ProtocolMapperModel mapping) {
- ProtocolMapperEntity entity = getProtocolClaimMapping(mapping.getId());
- entity.setProtocol(mapping.getProtocol());
- entity.setProtocolClaim(mapping.getProtocolClaim());
+ ProtocolMapperEntity entity = getProtocolMapper(mapping.getId());
+ entity.setProtocolMapper(mapping.getProtocolMapper());
entity.setAppliedByDefault(mapping.isAppliedByDefault());
- entity.setSource(mapping.getSource().name());
- entity.setSourceAttribute(mapping.getSourceAttribute());
+ entity.setConsentRequired(mapping.isConsentRequired());
+ entity.setConsentText(mapping.getConsentText());
+ if (entity.getConfig() == null) {
+ entity.setConfig(mapping.getConfig());
+ } else {
+ entity.getConfig().clear();
+ entity.getConfig().putAll(mapping.getConfig());
+ }
em.flush();
}
@Override
public ProtocolMapperModel getProtocolMapperById(String id) {
- ProtocolMapperEntity entity = getProtocolClaimMapping(id);
+ ProtocolMapperEntity entity = getProtocolMapper(id);
if (entity == null) return null;
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
+ mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
- mapping.setProtocolClaim(entity.getProtocolClaim());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(ProtocolMapperModel.Source.valueOf(entity.getSource()));
- mapping.setSourceAttribute(entity.getSourceAttribute());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) config.putAll(entity.getConfig());
+ mapping.setConfig(config);
return mapping;
}
}
\ No newline at end of file
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
index 59e077fb3f..bd8bbe2a0d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
@@ -1,14 +1,18 @@
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.JoinColumn;
import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
+import java.util.Map;
/**
* @author Bill Burke
@@ -25,18 +29,25 @@ public class ProtocolMapperEntity {
@Column(name="ID", length = 36)
protected String id;
- @Column(name = "PROTOCOL_CLAIM")
- protected String protocolClaim;
+ @Column(name="NAME")
+ protected String name;
+
@Column(name = "PROTOCOL")
protected String protocol;
- @Column(name = "SOURCE")
- protected String source;
- @Column(name = "SOURCE_ATTRIBUTE")
- protected String sourceAttribute;
@Column(name = "PROTOCOL_MAPPER_NAME")
protected String protocolMapper;
@Column(name = "APPLIED_BY_DEFAULT")
protected boolean appliedByDefault;
+ @Column(name="CONSENT_REQUIRED")
+ protected boolean consentRequired;
+ @Column(name="CONSENT_TEXT")
+ protected String consentText;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable(name="PROTOCOL_MAPPER_CONFIG", joinColumns={ @JoinColumn(name="PROTOCOL_MAPPER_ID") })
+ private Map config;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID")
@@ -50,12 +61,12 @@ public class ProtocolMapperEntity {
this.id = id;
}
- public String getProtocolClaim() {
- return protocolClaim;
+ public String getName() {
+ return name;
}
- public void setProtocolClaim(String protocolClaim) {
- this.protocolClaim = protocolClaim;
+ public void setName(String name) {
+ this.name = name;
}
public String getProtocol() {
@@ -66,22 +77,6 @@ public class ProtocolMapperEntity {
this.protocol = protocol;
}
- public String getSource() {
- return source;
- }
-
- public void setSource(String source) {
- this.source = source;
- }
-
- public String getSourceAttribute() {
- return sourceAttribute;
- }
-
- public void setSourceAttribute(String sourceAttribute) {
- this.sourceAttribute = sourceAttribute;
- }
-
public String getProtocolMapper() {
return protocolMapper;
}
@@ -98,6 +93,14 @@ public class ProtocolMapperEntity {
this.appliedByDefault = appliedByDefault;
}
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+
public RealmEntity getRealm() {
return realm;
}
@@ -106,6 +109,22 @@ public class ProtocolMapperEntity {
this.realm = realm;
}
+ public boolean isConsentRequired() {
+ return consentRequired;
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ this.consentRequired = consentRequired;
+ }
+
+ public String getConsentText() {
+ return consentText;
+ }
+
+ public void setConsentText(String consentText) {
+ this.consentText = consentText;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
index 691bce4925..3d6b735096 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java
@@ -8,6 +8,7 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmListenerHelper;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
@@ -27,10 +28,12 @@ public class MongoRealmProvider implements RealmProvider {
private final MongoStoreInvocationContext invocationContext;
private final KeycloakSession session;
+ protected RealmListenerHelper listeners;
- public MongoRealmProvider(KeycloakSession session, MongoStoreInvocationContext invocationContext) {
+ public MongoRealmProvider(KeycloakSession session, MongoStoreInvocationContext invocationContext, RealmListenerHelper listeners) {
this.session = session;
this.invocationContext = invocationContext;
+ this.listeners = listeners;
}
@Override
@@ -38,6 +41,18 @@ public class MongoRealmProvider implements RealmProvider {
// TODO
}
+ @Override
+ public void registerListener(RealmCreationListener listener) {
+ listeners.registerListener(listener);
+ }
+
+ @Override
+ public void unregisterListener(RealmCreationListener listener) {
+ listeners.unregisterListener(listener);
+
+ }
+
+
@Override
public RealmModel createRealm(String name) {
return createRealm(KeycloakModelUtils.generateId(), name);
@@ -51,7 +66,9 @@ public class MongoRealmProvider implements RealmProvider {
getMongoStore().insertEntity(newRealm, invocationContext);
- return new RealmAdapter(session, newRealm, invocationContext);
+ RealmModel model = new RealmAdapter(session, newRealm, invocationContext);
+ listeners.executeCreationListeners(model);
+ return model;
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java
index 96067533a4..c8f2f1186a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProviderFactory.java
@@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.mongo.MongoConnectionProvider;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmListenerHelper;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RealmProviderFactory;
@@ -15,6 +16,8 @@ import org.keycloak.models.RealmProviderFactory;
public class MongoRealmProviderFactory implements RealmProviderFactory {
protected static final Logger logger = Logger.getLogger(MongoRealmProviderFactory.class);
+ protected RealmListenerHelper listeners = new RealmListenerHelper();
+
@Override
public String getId() {
return "mongo";
@@ -27,7 +30,7 @@ public class MongoRealmProviderFactory implements RealmProviderFactory {
@Override
public RealmProvider create(KeycloakSession session) {
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
- return new MongoRealmProvider(session, connection.getInvocationContext());
+ return new MongoRealmProvider(session, connection.getInvocationContext(), listeners);
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 0dfac8be71..9b20f2a869 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -789,14 +789,19 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public Set getProtocolMappers() {
Set result = new HashSet();
- for (ProtocolMapperEntity entity : realm.getClaimMappings()) {
+ for (ProtocolMapperEntity entity : realm.getProtocolMappers()) {
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
- mapping.setProtocolClaim(entity.getProtocolClaim());
+ mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
- mapping.setSource(entity.getSource());
- mapping.setSourceAttribute(entity.getSourceAttribute());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) {
+ config.putAll(entity.getConfig());
+ }
+ mapping.setConfig(config);
}
return result;
}
@@ -806,30 +811,31 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
ProtocolMapperEntity entity = new ProtocolMapperEntity();
if (model.getId() != null) entity.setId(model.getId());
else entity.setId(KeycloakModelUtils.generateId());
- entity.setSourceAttribute(model.getSourceAttribute());
entity.setProtocol(model.getProtocol());
- entity.setProtocolClaim(model.getProtocolClaim());
- entity.setSource(model.getSource());
+ entity.setName(model.getName());
entity.setAppliedByDefault(model.isAppliedByDefault());
entity.setProtocolMapper(model.getProtocolMapper());
- realm.getClaimMappings().add(entity);
+ entity.setConfig(model.getConfig());
+ entity.setConsentRequired(model.isConsentRequired());
+ entity.setConsentText(model.getConsentText());
+ realm.getProtocolMappers().add(entity);
updateRealm();
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
- mapping.setProtocol(entity.getProtocol());
- mapping.setProtocolClaim(entity.getProtocolClaim());
- mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(entity.getSource());
- mapping.setSourceAttribute(entity.getSourceAttribute());
- mapping.setProtocolMapper(entity.getProtocolMapper());
+ mapping.setProtocol(model.getProtocol());
+ mapping.setAppliedByDefault(model.isAppliedByDefault());
+ mapping.setProtocolMapper(model.getProtocolMapper());
+ mapping.setConfig(model.getConfig());
+ mapping.setConsentText(model.getConsentText());
+ mapping.setConsentRequired(model.isConsentRequired());
return mapping;
}
@Override
public void removeProtocolMapper(ProtocolMapperModel mapping) {
- for (ProtocolMapperEntity entity : realm.getClaimMappings()) {
+ for (ProtocolMapperEntity entity : realm.getProtocolMappers()) {
if (entity.getId().equals(mapping.getId())) {
- realm.getClaimMappings().remove(entity);
+ realm.getProtocolMappers().remove(entity);
updateRealm();
break;
}
@@ -837,8 +843,8 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
}
- protected ProtocolMapperEntity getProtocolClaimMapping(String id) {
- for (ProtocolMapperEntity entity : realm.getClaimMappings()) {
+ protected ProtocolMapperEntity getProtocolMapper(String id) {
+ for (ProtocolMapperEntity entity : realm.getProtocolMappers()) {
if (entity.getId().equals(id)) {
return entity;
}
@@ -850,29 +856,36 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public void updateProtocolMapper(ProtocolMapperModel mapping) {
- ProtocolMapperEntity entity = getProtocolClaimMapping(mapping.getId());
- entity.setProtocol(mapping.getProtocol());
- entity.setProtocolClaim(mapping.getProtocolClaim());
+ ProtocolMapperEntity entity = getProtocolMapper(mapping.getId());
entity.setAppliedByDefault(mapping.isAppliedByDefault());
- entity.setSource(mapping.getSource());
- entity.setSourceAttribute(mapping.getSourceAttribute());
entity.setProtocolMapper(mapping.getProtocolMapper());
+ entity.setConsentRequired(mapping.isConsentRequired());
+ entity.setConsentText(mapping.getConsentText());
+ if (entity.getConfig() != null) {
+ entity.getConfig().clear();
+ entity.getConfig().putAll(mapping.getConfig());
+ } else {
+ entity.setConfig(mapping.getConfig());
+ }
updateRealm();
}
@Override
public ProtocolMapperModel getProtocolMapperById(String id) {
- ProtocolMapperEntity entity = getProtocolClaimMapping(id);
+ ProtocolMapperEntity entity = getProtocolMapper(id);
if (entity == null) return null;
ProtocolMapperModel mapping = new ProtocolMapperModel();
mapping.setId(entity.getId());
+ mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
- mapping.setProtocolClaim(entity.getProtocolClaim());
mapping.setAppliedByDefault(entity.isAppliedByDefault());
- mapping.setSource(entity.getSource());
- mapping.setSourceAttribute(entity.getSourceAttribute());
mapping.setProtocolMapper(entity.getProtocolMapper());
+ mapping.setConsentRequired(entity.isConsentRequired());
+ mapping.setConsentText(entity.getConsentText());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) config.putAll(config);
+ mapping.setConfig(config);
return mapping;
}
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
index 8697d72f20..c2028479f5 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
@@ -3,6 +3,8 @@ package org.keycloak.protocol;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
+import java.util.List;
+
/**
* @author Bill Burke
* @version $Revision: 1 $
@@ -10,4 +12,36 @@ import org.keycloak.provider.ProviderFactory;
public interface ProtocolMapper extends Provider, ProviderFactory {
String getProtocol();
String getDisplayType();
+
+ public static class ConfigProperty {
+ protected String name;
+ protected String label;
+ protected String helpText;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getHelpText() {
+ return helpText;
+ }
+
+ public void setHelpText(String helpText) {
+ this.helpText = helpText;
+ }
+ }
+
+ List getConfigProperties();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAttributeToTokenMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAttributeToTokenMapper.java
deleted file mode 100755
index 9ff207f324..0000000000
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAttributeToTokenMapper.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.keycloak.protocol.oidc;
-
-import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.representations.AccessToken;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Mappings user data to an ID Token claim. Source can be from UserModel.getAttributes(), a get method on UserModel, UserSession.getNote
- * or ClientSession.getNote. Claim can be a full qualified nested object name, i.e. "address.country". This will create a nested
- * json object within the toke claim.
- *
- * @author Bill Burke
- * @version $Revision: 1 $
- */
-public class OIDCAttributeToTokenMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenTransformer {
- @Override
- public String getId() {
- return "oidc-attribute-claim-mapper";
- }
-
- @Override
- public String getDisplayType() {
- return "Attribute Claim Mapper";
- }
-
- @Override
- public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
- UserSessionModel userSession, ClientSessionModel clientSession) {
- String attributeValue = null;
- UserModel user = userSession.getUser();
- switch (mappingModel.getSource()) {
- case USER_ATTRIBUTE:
- attributeValue = user.getAttribute(mappingModel.getSourceAttribute());
- break;
- case USER_SESSION_NOTE:
- attributeValue = userSession.getNote(mappingModel.getSourceAttribute());
- break;
- case CLIENT_SESSION_NOTE:
- attributeValue = clientSession.getNote(mappingModel.getSourceAttribute());
- break;
- case USER_MODEL:
- attributeValue = getUserModelValue(user, mappingModel);
- break;
- }
- if (attributeValue == null) return token;
- String[] split = mappingModel.getProtocolClaim().split(".");
- Map jsonObject = token.getOtherClaims();
- for (int i = 0; i < split.length; i++) {
- if (i == split.length - 1) {
- jsonObject.put(split[i], attributeValue);
- } else {
- Map nested = (Map)jsonObject.get(split[i]);
- if (nested == null) {
- nested = new HashMap();
- jsonObject.put(split[i], nested);
- jsonObject = nested;
- }
- }
- }
- return token;
- }
-
- protected String getUserModelValue(UserModel user, ProtocolMapperModel model) {
- String sourceAttribute = model.getSourceAttribute();
- if (sourceAttribute == null) return null;
-
- String methodName = "get" + Character.toUpperCase(sourceAttribute.charAt(0)) + sourceAttribute.substring(1);
- try {
- Method method = UserModel.class.getMethod(methodName);
- Object val = method.invoke(user);
- if (val != null) return val.toString();
- } catch (Exception ignore) {
-
- }
- return null;
- }
-}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index 3c4a800ad0..b0e3605080 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -20,6 +20,7 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
@Override
public void init(Config.Scope config) {
+ System.out.println("here");
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/AbstractOIDCProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java
similarity index 83%
rename from services/src/main/java/org/keycloak/protocol/oidc/AbstractOIDCProtocolMapper.java
rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java
index 9b3fb60192..dc949834d9 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/AbstractOIDCProtocolMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractOIDCProtocolMapper.java
@@ -1,8 +1,9 @@
-package org.keycloak.protocol.oidc;
+package org.keycloak.protocol.oidc.mappers;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.ProtocolMapper;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
/**
* @author Bill Burke
@@ -27,7 +28,6 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
@Override
public void init(Config.Scope config) {
-
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAccessTokenTransformer.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java
similarity index 84%
rename from services/src/main/java/org/keycloak/protocol/oidc/OIDCAccessTokenTransformer.java
rename to services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java
index c63385f297..e2c479fb4a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAccessTokenTransformer.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAccessTokenMapper.java
@@ -1,4 +1,4 @@
-package org.keycloak.protocol.oidc;
+package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@@ -10,7 +10,7 @@ import org.keycloak.representations.AccessToken;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public interface OIDCAccessTokenTransformer {
+public interface OIDCAccessTokenMapper {
AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCClientSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCClientSessionNoteMapper.java
new file mode 100755
index 0000000000..3e38ed29e4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCClientSessionNoteMapper.java
@@ -0,0 +1,60 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mappings UserSessionModel.note to an ID Token claim. Token claim name can be a full qualified nested object name,
+ * i.e. "address.country". This will create a nested
+ * json object within the toke claim.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OIDCClientSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+ private static final List configProperties = new ArrayList();
+ public static final String CLIENT_SESSION_NOTE = "ClientSession Note";
+
+ static {
+ ConfigProperty property;
+ property = new ConfigProperty();
+ property.setName(CLIENT_SESSION_NOTE);
+ property.setLabel(CLIENT_SESSION_NOTE);
+ property.setHelpText("Name of the note to map in the UserSessionModel");
+ configProperties.add(property);
+ property.setName(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setLabel(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ configProperties.add(property);
+
+ }
+
+ public List getConfigProperties() {
+ return configProperties;
+ }
+ @Override
+ public String getId() {
+ return "oidc-client-session-note-mapper";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "ClientSession Note Mapper";
+ }
+
+ @Override
+ public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionModel clientSession) {
+ String note = mappingModel.getConfig().get(CLIENT_SESSION_NOTE);
+ String noteValue = clientSession.getNote(note);
+ OIDCUserAttributeMapper.mapClaim(token, mappingModel, noteValue);
+ return token;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java
new file mode 100755
index 0000000000..1c687be3b5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserAttributeMapper.java
@@ -0,0 +1,88 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Mappings UserModel.attribute to an ID Token claim. Token claim name can be a full qualified nested object name,
+ * i.e. "address.country". This will create a nested
+ * json object within the toke claim.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OIDCUserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+
+ public static final String TOKEN_CLAIM_NAME = "Token Claim Name";
+ private static final List configProperties = new ArrayList();
+ public static final String USER_MODEL_ATTRIBUTE_NAME = "UserModel Attribute Name";
+
+ static {
+ ConfigProperty property;
+ property = new ConfigProperty();
+ property.setName(USER_MODEL_ATTRIBUTE_NAME);
+ property.setLabel(USER_MODEL_ATTRIBUTE_NAME);
+ property.setHelpText("Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.");
+ configProperties.add(property);
+ property.setName(TOKEN_CLAIM_NAME);
+ property.setLabel(TOKEN_CLAIM_NAME);
+ property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ configProperties.add(property);
+
+ }
+
+
+ public List getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return "oidc-usermodel-attribute-mapper";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "UserModel Attribute Mapper";
+ }
+
+ @Override
+ public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionModel clientSession) {
+ UserModel user = userSession.getUser();
+ String attributeName = mappingModel.getConfig().get(USER_MODEL_ATTRIBUTE_NAME);
+ String attributeValue = user.getAttribute(attributeName);
+ if (attributeValue == null) return token;
+ mapClaim(token, mappingModel, attributeValue);
+ return token;
+ }
+
+ protected static void mapClaim(AccessToken token, ProtocolMapperModel mappingModel, String attributeValue) {
+ if (attributeValue == null) return;
+ String protocolClaim = mappingModel.getConfig().get(TOKEN_CLAIM_NAME);
+ String[] split = protocolClaim.split(".");
+ Map jsonObject = token.getOtherClaims();
+ for (int i = 0; i < split.length; i++) {
+ if (i == split.length - 1) {
+ jsonObject.put(split[i], attributeValue);
+ } else {
+ Map nested = (Map)jsonObject.get(split[i]);
+ if (nested == null) {
+ nested = new HashMap();
+ jsonObject.put(split[i], nested);
+ jsonObject = nested;
+ }
+ }
+ }
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
new file mode 100755
index 0000000000..4af3178680
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
@@ -0,0 +1,79 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Mappings UserModel property (the property name of a getter method) to an ID Token claim. Token claim name can be a full qualified nested object name,
+ * i.e. "address.country". This will create a nested
+ * json object within the toke claim.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+ private static final List configProperties = new ArrayList();
+ public static final String USER_MODEL_PROPERTY = "UserModel Property";
+
+ static {
+ ConfigProperty property;
+ property = new ConfigProperty();
+ property.setName(USER_MODEL_PROPERTY);
+ property.setLabel(USER_MODEL_PROPERTY);
+ property.setHelpText("Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.");
+ configProperties.add(property);
+ property.setName(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setLabel(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ configProperties.add(property);
+
+ }
+
+
+ public List getConfigProperties() {
+ return configProperties;
+ }
+ @Override
+ public String getId() {
+ return "oidc-usermodel-property-mapper";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "UserModel Property Mapper";
+ }
+
+ @Override
+ public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionModel clientSession) {
+ UserModel user = userSession.getUser();
+ String propertyName = mappingModel.getConfig().get(USER_MODEL_PROPERTY);
+ String propertyValue = getUserModelValue(user,propertyName);
+ OIDCUserAttributeMapper.mapClaim(token, mappingModel, propertyValue);
+
+ return token;
+ }
+
+ protected String getUserModelValue(UserModel user, String propertyName) {
+
+ String methodName = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+ try {
+ Method method = UserModel.class.getMethod(methodName);
+ Object val = method.invoke(user);
+ if (val != null) return val.toString();
+ } catch (Exception ignore) {
+
+ }
+ return null;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java
new file mode 100755
index 0000000000..0069d5a25d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserSessionNoteMapper.java
@@ -0,0 +1,61 @@
+package org.keycloak.protocol.oidc.mappers;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.representations.AccessToken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mappings UserSessionModel.note to an ID Token claim. Token claim name can be a full qualified nested object name,
+ * i.e. "address.country". This will create a nested
+ * json object within the toke claim.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OIDCUserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+ private static final List configProperties = new ArrayList();
+ public static final String USER_SESSION_NOTE = "UserSession Note";
+
+ static {
+ ConfigProperty property;
+ property = new ConfigProperty();
+ property.setName(USER_SESSION_NOTE);
+ property.setLabel("UserSession Note");
+ property.setHelpText("Name of the note to map in the UserSessionModel");
+ configProperties.add(property);
+ property.setName(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setLabel(OIDCUserAttributeMapper.TOKEN_CLAIM_NAME);
+ property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ configProperties.add(property);
+
+ }
+
+ public List getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return "oidc-user-session-note-mapper";
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "UserSession Note Mapper";
+ }
+
+ @Override
+ public AccessToken transformToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionModel clientSession) {
+ String note = mappingModel.getConfig().get(USER_SESSION_NOTE);
+ String noteValue = userSession.getNote(note);
+ OIDCUserAttributeMapper.mapClaim(token, mappingModel, noteValue);
+ return token;
+ }
+
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
index 0310583b52..0f9da65a17 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper
@@ -1 +1 @@
-org.keycloak.protocol.oidc.OIDCAttributeToTokenMapper
\ No newline at end of file
+org.keycloak.protocol.oidc.mappers.OIDCUserAttributeMapper
\ No newline at end of file