keycloak-aplcache

rest interface for claim mapping

2/25/2015 1:34:51 PM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
index 4349a75..9ca74f0 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
@@ -30,7 +30,7 @@ public class ApplicationRepresentation {
     protected Integer nodeReRegistrationTimeout;
     protected Map<String, Integer> registeredNodes;
     protected List<String> allowedIdentityProviders;
-    protected Set<String> protocolMappers;
+    protected List<ClientProtocolMappingRepresentation> protocolMappers;
 
     public String getId() {
         return id;
@@ -200,11 +200,11 @@ public class ApplicationRepresentation {
         this.allowedIdentityProviders = allowedIdentityProviders;
     }
 
-    public Set<String> getProtocolMappers() {
+    public List<ClientProtocolMappingRepresentation> getProtocolMappers() {
         return protocolMappers;
     }
 
-    public void setProtocolMappers(Set<String> protocolMappers) {
+    public void setProtocolMappers(List<ClientProtocolMappingRepresentation> protocolMappers) {
         this.protocolMappers = protocolMappers;
     }
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientProtocolMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientProtocolMappingRepresentation.java
new file mode 100755
index 0000000..e5252b1
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientProtocolMappingRepresentation.java
@@ -0,0 +1,26 @@
+package org.keycloak.representations.idm;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientProtocolMappingRepresentation {
+    protected String protocol;
+    protected String name;
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
index dcb6ba8..aa095a7 100755
--- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
@@ -24,7 +24,7 @@ public class OAuthClientRepresentation {
     protected Boolean fullScopeAllowed;
     protected Boolean frontchannelLogout;
     protected List<String> allowedIdentityProviders;
-    protected Set<String> protocolClaimMappings;
+    protected List<ClientProtocolMappingRepresentation> protocolMappers;
 
 
     public String getId() {
@@ -147,11 +147,11 @@ public class OAuthClientRepresentation {
         this.allowedIdentityProviders = allowedIdentityProviders;
     }
 
-    public Set<String> getProtocolClaimMappings() {
-        return protocolClaimMappings;
+    public List<ClientProtocolMappingRepresentation> getProtocolMappers() {
+        return protocolMappers;
     }
 
-    public void setProtocolClaimMappings(Set<String> protocolClaimMappings) {
-        this.protocolClaimMappings = protocolClaimMappings;
+    public void setProtocolMappers(List<ClientProtocolMappingRepresentation> protocolMappers) {
+        this.protocolMappers = protocolMappers;
     }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index edcb44e..5354d04 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -105,7 +105,7 @@ public interface ClientModel {
     boolean hasIdentityProvider(String providerId);
 
     Set<ProtocolMapperModel> getProtocolMappers();
-    void addProtocolMappers(Set<String> mapperNames);
-    void removeProtocolMappers(Set<String> mapperNames);
-    void setProtocolMappers(Set<String> mapperNames);
+    void addProtocolMappers(Set<String> mapperIds);
+    void removeProtocolMappers(Set<String> mapperIds);
+    void setProtocolMappers(Set<String> mapperIds);
 }
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 510d72a..f70f825 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -249,7 +249,7 @@ public interface RealmModel extends RoleContainerModel {
     void removeProtocolMapper(ProtocolMapperModel mapping);
     void updateProtocolMapper(ProtocolMapperModel mapping);
     public ProtocolMapperModel getProtocolMapperById(String id);
-    public ProtocolMapperModel getProtocolMapperByName(String name);
+    public ProtocolMapperModel getProtocolMapperByName(String protocol, 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 949f974..522c965 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
@@ -19,6 +19,7 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.ClaimRepresentation;
 import org.keycloak.representations.idm.ClaimTypeRepresentation;
+import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
@@ -266,8 +267,12 @@ public class ModelToRepresentation {
         }
 
         if (!applicationModel.getProtocolMappers().isEmpty()) {
-            Set<String> mappings = new HashSet<String>();
-            for (ProtocolMapperModel model : applicationModel.getProtocolMappers()) mappings.add(model.getName());
+            List<ClientProtocolMappingRepresentation> mappings = new LinkedList<ClientProtocolMappingRepresentation>();
+            for (ProtocolMapperModel model : applicationModel.getProtocolMappers()) {
+                ClientProtocolMappingRepresentation map = new ClientProtocolMappingRepresentation();
+                map.setProtocol(model.getProtocol());
+                map.setName(model.getName());
+            }
             rep.setProtocolMappers(mappings);
         }
 
@@ -301,10 +306,15 @@ public class ModelToRepresentation {
         }
 
         if (!model.getProtocolMappers().isEmpty()) {
-            Set<String> mappings = new HashSet<String>();
-            for (ProtocolMapperModel mappingModel : model.getProtocolMappers()) mappings.add(mappingModel.getName());
-            rep.setProtocolClaimMappings(mappings);
+            List<ClientProtocolMappingRepresentation> mappings = new LinkedList<ClientProtocolMappingRepresentation>();
+            for (ProtocolMapperModel mapping : model.getProtocolMappers()) {
+                ClientProtocolMappingRepresentation map = new ClientProtocolMappingRepresentation();
+                map.setProtocol(mapping.getProtocol());
+                map.setName(mapping.getName());
+            }
+            rep.setProtocolMappers(mappings);
         }
+
         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 cc0b63d..9e152b8 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
@@ -23,6 +23,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.ClaimRepresentation;
 import org.keycloak.representations.idm.ClaimTypeRepresentation;
+import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
@@ -461,9 +462,18 @@ public class RepresentationToModel {
         }
 
         if (resourceRep.getProtocolMappers() != null) {
-            applicationModel.setProtocolMappers(resourceRep.getProtocolMappers());
+            Set<String> ids = new HashSet<String>();
+            for (ClientProtocolMappingRepresentation map : resourceRep.getProtocolMappers()) {
+                ProtocolMapperModel mapperModel = applicationModel.getRealm().getProtocolMapperByName(map.getProtocol(), map.getName());
+                if (mapperModel != null) {
+                    ids.add(mapperModel.getId());
+                }
+
+            }
+            applicationModel.setProtocolMappers(ids);
         }
 
+
         return applicationModel;
     }
 
@@ -637,8 +647,16 @@ public class RepresentationToModel {
             model.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
         }
 
-        if (rep.getProtocolClaimMappings() != null) {
-            model.addProtocolMappers(rep.getProtocolClaimMappings());
+        if (rep.getProtocolMappers() != null) {
+            Set<String> ids = new HashSet<String>();
+            for (ClientProtocolMappingRepresentation map : rep.getProtocolMappers()) {
+                ProtocolMapperModel mapperModel = model.getRealm().getProtocolMapperByName(map.getProtocol(), map.getName());
+                if (mapperModel != null) {
+                    ids.add(mapperModel.getId());
+                }
+
+            }
+            model.setProtocolMappers(ids);
         }
 
     }
@@ -777,7 +795,7 @@ public class RepresentationToModel {
             // we make sure we don't recreate mappers that are automatically created by the protocol providers.
             Set<ProtocolMapperModel> mappers = newRealm.getProtocolMappers();
             for (ProtocolMapperRepresentation representation : rep.getProtocolMappers()) {
-                ProtocolMapperModel existing = newRealm.getProtocolMapperByName(representation.getName());
+                ProtocolMapperModel existing = newRealm.getProtocolMapperByName(representation.getProtocol(), representation.getName());
                 if (existing == null) {
                     newRealm.addProtocolMapper(toModel(representation));
                 } else {
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index d036456..debd784 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -920,9 +920,9 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public ProtocolMapperModel getProtocolMapperByName(String name) {
+    public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
         for (ProtocolMapperModel mapping : cached.getClaimMappings()) {
-            if (mapping.getName().equals(name)) return mapping;
+            if (mapping.getProtocol().equals(protocol) && mapping.getName().equals(name)) return mapping;
         }
         return null;
     }
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 f45c085..d4616fd 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
@@ -380,9 +380,10 @@ public abstract class ClientAdapter implements ClientModel {
         return mappings;
     }
 
-    protected ProtocolMapperEntity findProtocolMapperByName(String name) {
-        TypedQuery<ProtocolMapperEntity> query = em.createNamedQuery("getProtocolMapperByName", ProtocolMapperEntity.class);
+    protected ProtocolMapperEntity findProtocolMapperByName(String protocol, String name) {
+        TypedQuery<ProtocolMapperEntity> query = em.createNamedQuery("getProtocolMapperByNameProtocol", ProtocolMapperEntity.class);
         query.setParameter("name", name);
+        query.setParameter("protocol", protocol);
         query.setParameter("realm", entity.getRealm());
         List<ProtocolMapperEntity> entities = query.getResultList();
         if (entities.size() == 0) return null;
@@ -396,11 +397,11 @@ public abstract class ClientAdapter implements ClientModel {
         Collection<ProtocolMapperEntity> entities = entity.getProtocolMappers();
         Set<String> already = new HashSet<String>();
         for (ProtocolMapperEntity rel : entities) {
-            already.add(rel.getName());
+            already.add(rel.getId());
         }
-        for (String name : mappings) {
-            if (!already.contains(name)) {
-                ProtocolMapperEntity mapping = findProtocolMapperByName(name);
+        for (String id : mappings) {
+            if (!already.contains(id)) {
+                ProtocolMapperEntity mapping = em.find(ProtocolMapperEntity.class, id);
                 if (mapping != null) {
                     entities.add(mapping);
                 }
@@ -414,7 +415,7 @@ public abstract class ClientAdapter implements ClientModel {
         Collection<ProtocolMapperEntity> entities = entity.getProtocolMappers();
         List<ProtocolMapperEntity> remove = new LinkedList<ProtocolMapperEntity>();
         for (ProtocolMapperEntity rel : entities) {
-            if (mappings.contains(rel.getName())) remove.add(rel);
+            if (mappings.contains(rel.getId())) remove.add(rel);
         }
         for (ProtocolMapperEntity entity : remove) {
             entities.remove(entity);
@@ -428,15 +429,15 @@ public abstract class ClientAdapter implements ClientModel {
         Set<String> already = new HashSet<String>();
         while (it.hasNext()) {
             ProtocolMapperEntity mapper = it.next();
-            if (mappings.contains(mapper.getName())) {
-                already.add(mapper.getName());
+            if (mappings.contains(mapper.getId())) {
+                already.add(mapper.getId());
                 continue;
             }
             it.remove();
         }
-        for (String name : mappings) {
-            if (!already.contains(name)) {
-                ProtocolMapperEntity mapping = findProtocolMapperByName(name);
+        for (String id : mappings) {
+            if (!already.contains(id)) {
+                ProtocolMapperEntity mapping = em.find(ProtocolMapperEntity.class, id);
                 if (mapping != null) {
                     entities.add(mapping);
                 }
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 822049b..cfb7939 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
@@ -20,7 +20,7 @@ import java.util.Map;
  */
 @Entity
 @NamedQueries({
-        @NamedQuery(name="getProtocolMapperByName", query="select mapper from ProtocolMapperEntity mapper where mapper.name = :name and mapper.realm = :realm")
+        @NamedQuery(name="getProtocolMapperByNameProtocol", query="select mapper from ProtocolMapperEntity mapper where mapper.protocol = :protocol and mapper.name = :name and mapper.realm = :realm")
 })
 @Table(name="PROTOCOL_MAPPER")
 public class ProtocolMapperEntity {
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 6c4af39..00a3fbb 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
@@ -632,7 +632,7 @@ public class RealmAdapter implements RealmModel {
         Set<String> adding = new HashSet<String>();
         for (ProtocolMapperEntity mapper : realm.getProtocolMappers()) {
             if (mapper.isAppliedByDefault()) {
-                adding.add(mapper.getName());
+                adding.add(mapper.getId());
             }
         }
         client.setProtocolMappers(adding);
@@ -1295,8 +1295,8 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
-        if (getProtocolMapperByName(model.getName()) != null) {
-            throw new RuntimeException("Duplicate protocol mapper with name: " + model.getName());
+        if (getProtocolMapperByName(model.getProtocol(), model.getName()) != null) {
+            throw new RuntimeException("protocol mapper name must be unique per protocol");
         }
         String id = KeycloakModelUtils.generateId();
         ProtocolMapperEntity entity = new ProtocolMapperEntity();
@@ -1325,9 +1325,9 @@ public class RealmAdapter implements RealmModel {
 
     }
 
-    protected ProtocolMapperEntity getProtocolMapperEntityByName(String name) {
+    protected ProtocolMapperEntity getProtocolMapperEntityByName(String protocol, String name) {
         for (ProtocolMapperEntity entity : realm.getProtocolMappers()) {
-            if (entity.getName().equals(name)) {
+            if (entity.getProtocol().equals(protocol) && entity.getName().equals(name)) {
                 return entity;
             }
         }
@@ -1370,8 +1370,8 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public ProtocolMapperModel getProtocolMapperByName(String name) {
-        ProtocolMapperEntity entity = getProtocolMapperEntityByName(name);
+    public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
+        ProtocolMapperEntity entity = getProtocolMapperEntityByName(protocol, name);
         if (entity == null) return null;
         return entityToModel(entity);
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 7bc47a5..8d87dc0 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -303,22 +303,22 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
     }
 
     @Override
-    public void addProtocolMappers(Set<String> mapperNames) {
-        getMongoEntityAsClient().getProtocolMappers().addAll(mapperNames);
+    public void addProtocolMappers(Set<String> mapperIds) {
+        getMongoEntityAsClient().getProtocolMappers().addAll(mapperIds);
         updateMongoEntity();
 
     }
 
     @Override
-    public void removeProtocolMappers(Set<String> mapperNames) {
-        getMongoEntityAsClient().getProtocolMappers().removeAll(mapperNames);
+    public void removeProtocolMappers(Set<String> mapperIds) {
+        getMongoEntityAsClient().getProtocolMappers().removeAll(mapperIds);
         updateMongoEntity();
     }
 
     @Override
-    public void setProtocolMappers(Set<String> mapperNames) {
+    public void setProtocolMappers(Set<String> mapperIds) {
         getMongoEntityAsClient().getProtocolMappers().clear();
-        getMongoEntityAsClient().getProtocolMappers().addAll(mapperNames);
+        getMongoEntityAsClient().getProtocolMappers().addAll(mapperIds);
         updateMongoEntity();
     }
 
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 065443f..d48ae7d 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
@@ -619,7 +619,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
     public void addDefaultClientProtocolMappers(ClientModel client) {
         Set<String> adding = new HashSet<String>();
         for (ProtocolMapperEntity mapper : realm.getProtocolMappers()) {
-            if (mapper.isAppliedByDefault()) adding.add(mapper.getName());
+            if (mapper.isAppliedByDefault()) adding.add(mapper.getId());
         }
         client.setProtocolMappers(adding);
 
@@ -820,6 +820,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
+        if (getProtocolMapperByName(model.getProtocol(), model.getName()) != null) {
+            throw new RuntimeException("protocol mapper name must be unique per protocol");
+        }
         ProtocolMapperEntity entity = new ProtocolMapperEntity();
         entity.setId(KeycloakModelUtils.generateId());
         entity.setProtocol(model.getProtocol());
@@ -855,9 +858,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         return null;
 
     }
-    protected ProtocolMapperEntity getProtocolMapperyEntityByName(String name) {
+    protected ProtocolMapperEntity getProtocolMapperEntityByName(String protocol, String name) {
         for (ProtocolMapperEntity entity : realm.getProtocolMappers()) {
-            if (entity.getName().equals(name)) {
+            if (entity.getProtocol().equals(protocol) && entity.getName().equals(name)) {
                 return entity;
             }
         }
@@ -891,8 +894,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
     }
 
     @Override
-    public ProtocolMapperModel getProtocolMapperByName(String name) {
-        ProtocolMapperEntity entity = getProtocolMapperyEntityById(name);
+    public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
+        ProtocolMapperEntity entity = getProtocolMapperEntityByName(protocol, name);
         if (entity == null) return null;
         return entityToModel(entity);
     }
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 08ee15f..4b60b6a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -91,7 +91,7 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
                 false);
 
         ProtocolMapperModel fullName = new ProtocolMapperModel();
-        if (realm.getProtocolMapperByName("full name") == null) {
+        if (realm.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "full name") == null) {
             fullName.setName("full name");
             fullName.setProtocolMapper(OIDCFullNameMapper.PROVIDER_ID);
             fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -102,7 +102,7 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
         }
 
         ProtocolMapperModel address = new ProtocolMapperModel();
-        if (realm.getProtocolMapperByName("address") == null) {
+        if (realm.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "address") == null) {
             address.setName("address");
             address.setProtocolMapper(OIDCAddressMapper.PROVIDER_ID);
             address.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -120,7 +120,7 @@ public class OIDCLoginProtocolFactory implements LoginProtocolFactory {
                                   String tokenClaimName, String claimType,
                                   boolean consentRequired, String consentText,
                                   boolean appliedByDefault) {
-        ProtocolMapperModel mapper = realm.getProtocolMapperByName(name);
+        ProtocolMapperModel mapper = realm.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, name);
         if (mapper != null) return;
         mapper = new ProtocolMapperModel();
         mapper.setName(name);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index 5e5a242..583d886 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -87,6 +88,14 @@ public class ApplicationResource {
         return new ClaimResource(application, auth);
     }
 
+    @Path("protocol-mappers")
+    public ClientProtocolMappersResource getProtocolMappers() {
+        ClientProtocolMappersResource mappers = new ClientProtocolMappersResource(realm, auth, application);
+        ResteasyProviderFactory.getInstance().injectProperties(mappers);
+        //resourceContext.initResource(mappers);
+        return mappers;
+    }
+
     /**
      * Update the application.
      * @param rep
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientProtocolMappersResource.java
new file mode 100755
index 0000000..b4d9456
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientProtocolMappersResource.java
@@ -0,0 +1,134 @@
+package org.keycloak.services.resources.admin;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Base resource for managing users
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientProtocolMappersResource {
+    protected static final Logger logger = Logger.getLogger(ClientProtocolMappersResource.class);
+    protected ClientModel client;
+    protected RealmModel realm;
+    protected RealmAuth auth;
+
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected KeycloakSession session;
+
+    public ClientProtocolMappersResource(RealmModel realm, RealmAuth auth, ClientModel client) {
+        this.auth = auth;
+        this.realm = realm;
+        this.client = client;
+
+        auth.init(RealmAuth.Resource.USER);
+    }
+
+    /**
+     * Map of mappers by name for a specific protocol attached to the client
+     *
+     * @param protocol
+     * @return
+     */
+    @GET
+    @NoCache
+    @Path("protocol/{protocol}")
+    @Produces("application/json")
+    public Map<String, ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) {
+        auth.requireView();
+        Map<String, ProtocolMapperRepresentation> mappers = new HashMap<String, ProtocolMapperRepresentation>();
+        for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
+            mappers.put(mapper.getName(), ModelToRepresentation.toRepresentation(mapper));
+        }
+        return mappers;
+    }
+
+    /**
+     * Add mappers to client.
+     *
+     * @param mapperIds List of mapper ids
+     */
+    @Path("models/add")
+    @PUT
+    @NoCache
+    @Consumes("application/json")
+    public void addMappers(Set<String> mapperIds) {
+        auth.requireManage();
+        client.addProtocolMappers(mapperIds);
+    }
+
+    /**
+     * replace sets of client mappers.
+     *
+     * @param mapperIds  List of mapper ids
+     */
+    @Path("models/set")
+    @PUT
+    @NoCache
+    @Consumes("application/json")
+    public void setMappers(Set<String> mapperIds) {
+        auth.requireManage();
+        client.setProtocolMappers(mapperIds);
+    }
+
+    /**
+     * remove client mappers.
+     *
+     * @param mapperIds  List of mapper ids
+     */
+    @Path("models/remove")
+    @PUT
+    @NoCache
+    @Consumes("application/json")
+    public void removeMappers(Set<String> mapperIds) {
+        auth.requireManage();
+        client.removeProtocolMappers(mapperIds);
+    }
+
+    @GET
+    @NoCache
+    @Path("models")
+    @Produces("application/json")
+    public List<ProtocolMapperRepresentation> getMappersPerProtocol() {
+        auth.requireView();
+        List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
+        for (ProtocolMapperModel mapper : realm.getProtocolMappers()) {
+            mappers.add(ModelToRepresentation.toRepresentation(mapper));
+        }
+        return mappers;
+    }
+
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index 1bdf410..e5768e6 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -3,6 +3,7 @@ package org.keycloak.services.resources.admin;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.OAuthClientModel;
@@ -74,6 +75,19 @@ public class OAuthClientResource  {
     }
 
     /**
+     * interface for updating attached ProtocolMappers
+     *
+     * @return
+     */
+    @Path("protocol-mappers")
+    public ClientProtocolMappersResource getProtocolMappers() {
+        ClientProtocolMappersResource mappers = new ClientProtocolMappersResource(realm, auth, oauthClient);
+        ResteasyProviderFactory.getInstance().injectProperties(mappers);
+        //resourceContext.initResource(mappers);
+        return mappers;
+    }
+
+    /**
      *
      * @param attributePrefix
      * @return
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
new file mode 100755
index 0000000..2a7c82a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
@@ -0,0 +1,150 @@
+package org.keycloak.services.resources.admin;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.models.KerberosConstants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
+import org.keycloak.representations.idm.UserFederationProviderRepresentation;
+import org.keycloak.services.managers.UsersSyncManager;
+import org.keycloak.timer.TimerProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base resource for managing users
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProtocolMappersResource {
+    protected static final Logger logger = Logger.getLogger(ProtocolMappersResource.class);
+
+    protected RealmModel realm;
+
+    protected  RealmAuth auth;
+
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected KeycloakSession session;
+
+    public ProtocolMappersResource(RealmModel realm, RealmAuth auth) {
+        this.auth = auth;
+        this.realm = realm;
+
+        auth.init(RealmAuth.Resource.USER);
+    }
+
+    /**
+     * Map of mappers by name for a specific protocol
+     *
+     * @param protocol
+     * @return
+     */
+    @GET
+    @NoCache
+    @Path("protocol/{protocol}")
+    @Produces("application/json")
+    public Map<String, ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) {
+        auth.requireView();
+        Map<String, ProtocolMapperRepresentation> mappers = new HashMap<String, ProtocolMapperRepresentation>();
+        for (ProtocolMapperModel mapper : realm.getProtocolMappers()) {
+            mappers.put(mapper.getName(), ModelToRepresentation.toRepresentation(mapper));
+        }
+        return mappers;
+    }
+
+    /**
+     * createa mapper
+     *
+     * @param rep
+     */
+    @Path("models")
+    @POST
+    @NoCache
+    @Consumes("application/json")
+    public Response createMapper(ProtocolMapperRepresentation rep) {
+        auth.requireManage();
+        ProtocolMapperModel model = RepresentationToModel.toModel(rep);
+        realm.addProtocolMapper(model);
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
+    }
+
+    @GET
+    @NoCache
+    @Path("models")
+    @Produces("application/json")
+    public List<ProtocolMapperRepresentation> getMappersPerProtocol() {
+        auth.requireView();
+        List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
+        for (ProtocolMapperModel mapper : realm.getProtocolMappers()) {
+            mappers.add(ModelToRepresentation.toRepresentation(mapper));
+        }
+        return mappers;
+    }
+
+    @GET
+    @NoCache
+    @Path("models/{id}")
+    @Produces("application/json")
+    public ProtocolMapperRepresentation getMapperById(@PathParam("id") String id) {
+        auth.requireView();
+        ProtocolMapperModel model = realm.getProtocolMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        return ModelToRepresentation.toRepresentation(model);
+    }
+
+    @PUT
+    @NoCache
+    @Path("models/{id}")
+    @Consumes("application/json")
+    public void update(@PathParam("id") String id, ProtocolMapperRepresentation rep) {
+        auth.requireManage();
+        ProtocolMapperModel model = realm.getProtocolMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        model = RepresentationToModel.toModel(rep);
+        realm.updateProtocolMapper(model);
+    }
+
+    @DELETE
+    @NoCache
+    @Path("models/{id}")
+    public void delete(@PathParam("id") String id) {
+        auth.requireManage();
+        ProtocolMapperModel model = realm.getProtocolMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        realm.removeProtocolMapper(model);
+    }
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 3e5c2b0..592fa6f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -274,6 +274,19 @@ public class RealmAdminResource {
     }
 
     /**
+     * Protocol mappers
+     *
+     */
+    @Path("protocol-mappers")
+    @POST
+    public ProtocolMappersResource protocolMappers() {
+        ProtocolMappersResource mappers = new ProtocolMappersResource(realm, auth);
+        ResteasyProviderFactory.getInstance().injectProperties(mappers);
+        //resourceContext.initResource(mappers);
+        return mappers;
+    }
+
+    /**
      * Removes all user sessions.  Any application that has an admin url will also be told to invalidate any sessions
      * they have.
      *
@@ -281,7 +294,6 @@ public class RealmAdminResource {
     @Path("logout-all")
     @POST
     public GlobalRequestResult logoutAll() {
-        auth.requireManage();
         session.sessions().removeUserSessions(realm);
         return new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 2ac929d..ecde0fd 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -119,7 +119,6 @@ public class ServerInfoAdminResource {
         }
     }
 
-
     private void setProtocols(ServerInfoRepresentation info) {
         info.protocols = new LinkedList<String>();
         for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {