keycloak-aplcache

more claims work

2/21/2015 1:26:55 PM

Changes

Details

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 0de32d8..71fc2c4 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 @@
             <column name="ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
-            <column name="PROTOCOL_CLAIM" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)"/>
             <column name="PROTOCOL" type="VARCHAR(255)"/>
             <column name="SOURCE" type="VARCHAR(255)"/>
             <column name="PROTOCOL_MAPPER_NAME" type="VARCHAR(255)"/>
-            <column name="SOURCE_ATTRIBUTE" type="VARCHAR(255)"/>
             <column name="APPLIED_BY_DEFAULT" type="BOOLEAN(1)"/>
+            <column name="CONSENT_REQUIRED" type="BOOLEAN(1)"/>
+            <column name="CONSENT_TEXT" type="VARCHAR(255)"/>
             <column name="REALM_ID" type="VARCHAR(36)"/>
         </createTable>
+        <createTable tableName="PROTOCOL_MAPPER_CONFIG">
+            <column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="CLOB"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
         <createTable tableName="CLAIM_TYPE">
             <column name="ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
@@ -80,11 +90,13 @@
         <addPrimaryKey columnNames="INTERNAL_ID" constraintName="CONSTRAINT_2B" tableName="IDENTITY_PROVIDER"/>
         <addPrimaryKey columnNames="IDENTITY_PROVIDER, USER_ID" constraintName="CONSTRAINT_40" tableName="FEDERATED_IDENTITY"/>
         <addPrimaryKey columnNames="IDENTITY_PROVIDER_ID, NAME" constraintName="CONSTRAINT_D" tableName="IDENTITY_PROVIDER_CONFIG"/>
+        <addPrimaryKey columnNames="PROTOCOL_MAPPER_ID, NAME" constraintName="CONSTRAINT_PMConfig" tableName="PROTOCOL_MAPPER_CONFIG"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLAIM_TYPE" constraintName="FK_CT_REALM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="PROTOCOL_MAPPER" constraintName="FK_PCM_REALM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
         <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
         <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
+        <addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
         <addForeignKeyConstraint baseColumnNames="INTERNAL_ID" baseTableName="CLIENT_ALLOWED_IDENTITY_PROVIDER" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
         <addUniqueConstraint columnNames="INTERNAL_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER"/>
         <addForeignKeyConstraint baseColumnNames="MAPPING_ID" baseTableName="CLIENT_PROTOCOL_MAPPER" constraintName="FK_CPCM" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
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 e721941..4f132e1 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @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<String, String> config = new HashMap<String, String>();
 
 
     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<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> 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 d4ef642..21cc77a 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/entities/ProtocolMapperEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ProtocolMapperEntity.java
index de8f793..13f4cfc 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @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<String, String> 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<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> 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 7db0bac..b511a32 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<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
     private List<IdentityProviderEntity> identityProviders = new ArrayList<IdentityProviderEntity>();
     private List<ClaimTypeEntity> claimTypes = new ArrayList<ClaimTypeEntity>();
-    private List<ProtocolMapperEntity> claimMappings = new ArrayList<ProtocolMapperEntity>();
+    private List<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
 
     private Map<String, String> browserSecurityHeaders = new HashMap<String, String>();
     private Map<String, String> smtpConfig = new HashMap<String, String>();
@@ -401,12 +401,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.claimTypes = claimTypes;
     }
 
-    public List<ProtocolMapperEntity> getClaimMappings() {
-        return claimMappings;
+    public List<ProtocolMapperEntity> getProtocolMappers() {
+        return protocolMappers;
     }
 
-    public void setClaimMappings(List<ProtocolMapperEntity> claimMappings) {
-        this.claimMappings = claimMappings;
+    public void setProtocolMappers(List<ProtocolMapperEntity> protocolMappers) {
+        this.protocolMappers = protocolMappers;
     }
 }
 
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 001bb96..f27a9ea 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<String, String> 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<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
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 0000000..b7c074d
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RealmListenerHelper {
+    protected LinkedList<RealmProvider.RealmCreationListener> list = new LinkedList<RealmProvider.RealmCreationListener>();
+
+    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 4ec3c87..2c04cc1 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/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 5d0c582..69d5477 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<String, String> config = new HashMap<String, String>();
+        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 6c41613..2fb49af 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<String, ApplicationModel> 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 a1d232e..9419c91 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
@@ -51,6 +51,17 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
     }
 
     @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 e7b1551..9896e51 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
@@ -39,6 +39,16 @@ public class NoCacheRealmProvider implements CacheRealmProvider {
     }
 
     @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 9faace4..c06cdec 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<String, String> config = new HashMap<String, String>();
+            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/entities/ProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
index 59e077f..bd8bbe2 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -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<String, String> 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<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> 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/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 755fb6b..5529b83 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 7e090c2..4869ca7 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 f294bbd..7578fd0 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<String, String> config = new HashMap<String, String>();
+            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<String, String> config = new HashMap<String, String>();
+        if (entity.getConfig() != null) config.putAll(entity.getConfig());
+        mapping.setConfig(config);
         return mapping;
     }
 }
\ No newline at end of file
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 691bce4..3d6b735 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
@@ -39,6 +42,18 @@ public class MongoRealmProvider implements RealmProvider {
     }
 
     @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 9606753..c8f2f11 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 0dfac8b..9b20f2a 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<MongoRealmEntity> impleme
     @Override
     public Set<ProtocolMapperModel> getProtocolMappers() {
         Set<ProtocolMapperModel> result = new HashSet<ProtocolMapperModel>();
-        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<String, String> config = new HashMap<String, String>();
+            if (entity.getConfig() != null) {
+                config.putAll(entity.getConfig());
+            }
+            mapping.setConfig(config);
         }
         return result;
     }
@@ -806,30 +811,31 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> 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<MongoRealmEntity> 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<MongoRealmEntity> 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<String, String> config = new HashMap<String, String>();
+        if (entity.getConfig() != null) config.putAll(config);
+        mapping.setConfig(config);
         return mapping;
     }
 
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 0000000..3e38ed2
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCClientSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    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<ConfigProperty> 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/OIDCUserModelMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCUserModelMapper.java
new file mode 100755
index 0000000..4af3178
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCUserModelMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    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<ConfigProperty> 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 0000000..0069d5a
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCUserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
+    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    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<ConfigProperty> 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/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index 3c4a800..b0e3605 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/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
index 8697d72..c202847 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -10,4 +12,36 @@ import org.keycloak.provider.ProviderFactory;
 public interface ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper> {
     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<ConfigProperty> getConfigProperties();
 }
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 0310583..0f9da65 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