keycloak-aplcache

Merge pull request #1974 from patriot1burke/master set

1/5/2016 1:03:48 PM

Changes

Details

diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml
index 068d4e7..d0893a3 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml
@@ -18,6 +18,39 @@
             <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
                 <constraints nullable="false"/>
             </column>
+            <column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="STANDARD_FLOW_ENABLED" type="BOOLEAN" defaultValueBoolean="true">
+                <constraints nullable="false"/>
+            </column>
+            <column name="IMPLICIT_FLOW_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="DIRECT_ACCESS_GRANTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="SERVICE_ACCOUNTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="BEARER_ONLY" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="PUBLIC_CLIENT" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="CLIENT_TEMPLATE_ATTRIBUTES">
+            <column name="TEMPLATE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(2048)"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
         </createTable>
         <createTable tableName="TEMPLATE_SCOPE_MAPPING">
             <column name="TEMPLATE_ID" type="VARCHAR(36)">
@@ -69,6 +102,8 @@
         <addPrimaryKey columnNames="TEMPLATE_ID, ROLE_ID" constraintName="PK_TEMPLATE_SCOPE" tableName="TEMPLATE_SCOPE_MAPPING"/>
         <addForeignKeyConstraint baseColumnNames="TEMPLATE_ID" baseTableName="TEMPLATE_SCOPE_MAPPING" constraintName="FK_TEMPL_SCOPE_TEMPL" referencedColumnNames="ID" referencedTableName="CLIENT_TEMPLATE"/>
         <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="TEMPLATE_SCOPE_MAPPING" constraintName="FK_TEMPL_SCOPE_ROLE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addPrimaryKey columnNames="TEMPLATE_ID, NAME" constraintName="PK_CL_TMPL_ATTR" tableName="CLIENT_TEMPLATE_ATTRIBUTES"/>
+        <addForeignKeyConstraint baseColumnNames="TEMPLATE_ID" baseTableName="CLIENT_TEMPLATE_ATTRIBUTES" constraintName="FK_CL_TEMPL_ATTR_TEMPL" referencedColumnNames="ID" referencedTableName="CLIENT_TEMPLATE"/>
 
 
     </changeSet>
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
index dc575c4..e478c27 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
@@ -17,6 +17,16 @@ public class ClientTemplateRepresentation {
     protected String description;
     protected String protocol;
     protected Boolean fullScopeAllowed;
+    protected Boolean bearerOnly;
+    protected Boolean consentRequired;
+    protected Boolean standardFlowEnabled;
+    protected Boolean implicitFlowEnabled;
+    protected Boolean directAccessGrantsEnabled;
+    protected Boolean serviceAccountsEnabled;
+    protected Boolean publicClient;
+    protected Boolean frontchannelLogout;
+    protected Map<String, String> attributes;
+
     protected List<ProtocolMapperRepresentation> protocolMappers;
 
     public String getId() {
@@ -67,4 +77,76 @@ public class ClientTemplateRepresentation {
     public void setFullScopeAllowed(Boolean fullScopeAllowed) {
         this.fullScopeAllowed = fullScopeAllowed;
     }
+
+    public Boolean isBearerOnly() {
+        return bearerOnly;
+    }
+
+    public void setBearerOnly(Boolean bearerOnly) {
+        this.bearerOnly = bearerOnly;
+    }
+
+    public Boolean isConsentRequired() {
+        return consentRequired;
+    }
+
+    public void setConsentRequired(Boolean consentRequired) {
+        this.consentRequired = consentRequired;
+    }
+
+    public Boolean isStandardFlowEnabled() {
+        return standardFlowEnabled;
+    }
+
+    public void setStandardFlowEnabled(Boolean standardFlowEnabled) {
+        this.standardFlowEnabled = standardFlowEnabled;
+    }
+
+    public Boolean isImplicitFlowEnabled() {
+        return implicitFlowEnabled;
+    }
+
+    public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) {
+        this.implicitFlowEnabled = implicitFlowEnabled;
+    }
+
+    public Boolean isDirectAccessGrantsEnabled() {
+        return directAccessGrantsEnabled;
+    }
+
+    public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) {
+        this.directAccessGrantsEnabled = directAccessGrantsEnabled;
+    }
+
+    public Boolean isServiceAccountsEnabled() {
+        return serviceAccountsEnabled;
+    }
+
+    public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
+        this.serviceAccountsEnabled = serviceAccountsEnabled;
+    }
+
+    public Boolean isPublicClient() {
+        return publicClient;
+    }
+
+    public void setPublicClient(Boolean publicClient) {
+        this.publicClient = publicClient;
+    }
+
+    public Boolean isFrontchannelLogout() {
+        return frontchannelLogout;
+    }
+
+    public void setFrontchannelLogout(Boolean frontchannelLogout) {
+        this.frontchannelLogout = frontchannelLogout;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java b/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java
new file mode 100755
index 0000000..8ec8728
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java
@@ -0,0 +1,55 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientConfigResolver {
+    protected ClientModel client;
+    protected ClientTemplateModel clientTemplate;
+
+    public ClientConfigResolver(ClientModel client) {
+        this.client = client;
+        this.clientTemplate = client.getClientTemplate();
+    }
+
+    public String resolveAttribute(String name) {
+        if (clientTemplate != null && client.useTemplateConfig()) {
+            return clientTemplate.getAttribute(name);
+        } else {
+            return client.getAttribute(name);
+        }
+    }
+
+    public boolean isFrontchannelLogout() {
+        if (clientTemplate != null && client.useTemplateConfig()) {
+            return clientTemplate.isFrontchannelLogout();
+        }
+
+        return client.isFrontchannelLogout();
+    }
+
+    boolean isConsentRequired() {
+        if (clientTemplate != null && client.useTemplateConfig()) {
+            return clientTemplate.isConsentRequired();
+        }
+
+        return client.isConsentRequired();
+    }
+
+    boolean isStandardFlowEnabled() {
+        if (clientTemplate != null && client.useTemplateConfig()) {
+            return clientTemplate.isStandardFlowEnabled();
+        }
+
+        return client.isStandardFlowEnabled();
+    }
+
+    boolean isServiceAccountsEnabled() {
+        if (clientTemplate != null && client.useTemplateConfig()) {
+            return clientTemplate.isServiceAccountsEnabled();
+        }
+
+        return client.isServiceAccountsEnabled();
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java b/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java
index f3c0f59..eb3fbc0 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java
@@ -23,5 +23,35 @@ public interface ClientTemplateModel extends ProtocolMapperContainerModel, Scope
     String getProtocol();
     void setProtocol(String protocol);
 
+    void setAttribute(String name, String value);
+    void removeAttribute(String name);
+    String getAttribute(String name);
+    Map<String, String> getAttributes();
+
+    boolean isFrontchannelLogout();
+    void setFrontchannelLogout(boolean flag);
+
+    boolean isBearerOnly();
+    void setBearerOnly(boolean only);
+
+    boolean isPublicClient();
+    void setPublicClient(boolean flag);
+
+    boolean isConsentRequired();
+    void setConsentRequired(boolean consentRequired);
+
+    boolean isStandardFlowEnabled();
+    void setStandardFlowEnabled(boolean standardFlowEnabled);
+
+    boolean isImplicitFlowEnabled();
+    void setImplicitFlowEnabled(boolean implicitFlowEnabled);
+
+    boolean isDirectAccessGrantsEnabled();
+    void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled);
+
+    boolean isServiceAccountsEnabled();
+    void setServiceAccountsEnabled(boolean serviceAccountsEnabled);
+
+
 
 }
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java
index 8ca932f..a9879f7 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java
@@ -15,8 +15,17 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity {
     private String realmId;
     private String protocol;
     private boolean fullScopeAllowed;
-    private List<String> scopeIds = new ArrayList<String>();
-    private List<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
+    private boolean bearerOnly;
+    private boolean consentRequired;
+    private boolean standardFlowEnabled;
+    private boolean implicitFlowEnabled;
+    private boolean directAccessGrantsEnabled;
+    private boolean serviceAccountsEnabled;
+    private boolean publicClient;
+    private boolean frontchannelLogout;
+    private List<String> scopeIds = new ArrayList<>();
+    private List<ProtocolMapperEntity> protocolMappers = new ArrayList<>();
+    private Map<String, String> attributes = new HashMap<>();
 
     public String getName() {
         return name;
@@ -73,5 +82,77 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity {
     public void setScopeIds(List<String> scopeIds) {
         this.scopeIds = scopeIds;
     }
+
+    public boolean isBearerOnly() {
+        return bearerOnly;
+    }
+
+    public void setBearerOnly(boolean bearerOnly) {
+        this.bearerOnly = bearerOnly;
+    }
+
+    public boolean isConsentRequired() {
+        return consentRequired;
+    }
+
+    public void setConsentRequired(boolean consentRequired) {
+        this.consentRequired = consentRequired;
+    }
+
+    public boolean isStandardFlowEnabled() {
+        return standardFlowEnabled;
+    }
+
+    public void setStandardFlowEnabled(boolean standardFlowEnabled) {
+        this.standardFlowEnabled = standardFlowEnabled;
+    }
+
+    public boolean isImplicitFlowEnabled() {
+        return implicitFlowEnabled;
+    }
+
+    public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
+        this.implicitFlowEnabled = implicitFlowEnabled;
+    }
+
+    public boolean isDirectAccessGrantsEnabled() {
+        return directAccessGrantsEnabled;
+    }
+
+    public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
+        this.directAccessGrantsEnabled = directAccessGrantsEnabled;
+    }
+
+    public boolean isServiceAccountsEnabled() {
+        return serviceAccountsEnabled;
+    }
+
+    public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+        this.serviceAccountsEnabled = serviceAccountsEnabled;
+    }
+
+    public boolean isPublicClient() {
+        return publicClient;
+    }
+
+    public void setPublicClient(boolean publicClient) {
+        this.publicClient = publicClient;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public boolean isFrontchannelLogout() {
+        return frontchannelLogout;
+    }
+
+    public void setFrontchannelLogout(boolean frontchannelLogout) {
+        this.frontchannelLogout = frontchannelLogout;
+    }
 }
 
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 2432fc1..3b2cfdc 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
@@ -903,7 +903,7 @@ public class RepresentationToModel {
             }
         }
         if (resourceRep.isUseTemplateConfig() != null) client.setUseTemplateConfig(resourceRep.isUseTemplateConfig());
-        else client.setUseTemplateConfig(resourceRep.getClientTemplate() != null);
+        else client.setUseTemplateConfig(false); // default to false for now
 
         if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope());
         else client.setUseTemplateScope(resourceRep.getClientTemplate() != null);
@@ -1022,6 +1022,23 @@ public class RepresentationToModel {
                 client.addProtocolMapper(toModel(mapper));
             }
         }
+        if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
+        if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
+
+        if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
+        if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
+        if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
+        if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
+
+        if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
+        if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
+
+        if (resourceRep.getAttributes() != null) {
+            for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) {
+                client.setAttribute(entry.getKey(), entry.getValue());
+            }
+        }
+
 
         return client;
     }
@@ -1035,6 +1052,23 @@ public class RepresentationToModel {
 
 
         if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
+
+        if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
+        if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
+        if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled());
+        if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled());
+        if (rep.isDirectAccessGrantsEnabled() != null) resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled());
+        if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled());
+        if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
+        if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
+        if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout());
+
+        if (rep.getAttributes() != null) {
+            for (Map.Entry<String, String> entry : rep.getAttributes().entrySet()) {
+                resource.setAttribute(entry.getKey(), entry.getValue());
+            }
+        }
+
     }
 
     public static long getClaimsMask(ClaimRepresentation rep) {
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java
index 13b68aa..d403adc 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java
@@ -194,6 +194,127 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
        return false;
     }
 
+    public boolean isPublicClient() {
+        if (updated != null) return updated.isPublicClient();
+        return cached.isPublicClient();
+    }
+
+    public void setPublicClient(boolean flag) {
+        getDelegateForUpdate();
+        updated.setPublicClient(flag);
+    }
+
+    public boolean isFrontchannelLogout() {
+        if (updated != null) return updated.isPublicClient();
+        return cached.isFrontchannelLogout();
+    }
+
+    public void setFrontchannelLogout(boolean flag) {
+        getDelegateForUpdate();
+        updated.setFrontchannelLogout(flag);
+    }
+
+    @Override
+    public void setAttribute(String name, String value) {
+        getDelegateForUpdate();
+        updated.setAttribute(name, value);
+
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        getDelegateForUpdate();
+        updated.removeAttribute(name);
+
+    }
+
+    @Override
+    public String getAttribute(String name) {
+        if (updated != null) return updated.getAttribute(name);
+        return cached.getAttributes().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        if (updated != null) return updated.getAttributes();
+        Map<String, String> copy = new HashMap<String, String>();
+        copy.putAll(cached.getAttributes());
+        return copy;
+    }
+
+    @Override
+    public boolean isBearerOnly() {
+        if (updated != null) return updated.isBearerOnly();
+        return cached.isBearerOnly();
+    }
+
+    @Override
+    public void setBearerOnly(boolean only) {
+        getDelegateForUpdate();
+        updated.setBearerOnly(only);
+    }
+
+    @Override
+    public boolean isConsentRequired() {
+        if (updated != null) return updated.isConsentRequired();
+        return cached.isConsentRequired();
+    }
+
+    @Override
+    public void setConsentRequired(boolean consentRequired) {
+        getDelegateForUpdate();
+        updated.setConsentRequired(consentRequired);
+    }
+
+    @Override
+    public boolean isStandardFlowEnabled() {
+        if (updated != null) return updated.isStandardFlowEnabled();
+        return cached.isStandardFlowEnabled();
+    }
+
+    @Override
+    public void setStandardFlowEnabled(boolean standardFlowEnabled) {
+        getDelegateForUpdate();
+        updated.setStandardFlowEnabled(standardFlowEnabled);
+    }
+
+    @Override
+    public boolean isImplicitFlowEnabled() {
+        if (updated != null) return updated.isImplicitFlowEnabled();
+        return cached.isImplicitFlowEnabled();
+    }
+
+    @Override
+    public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
+        getDelegateForUpdate();
+        updated.setImplicitFlowEnabled(implicitFlowEnabled);
+    }
+
+    @Override
+    public boolean isDirectAccessGrantsEnabled() {
+        if (updated != null) return updated.isDirectAccessGrantsEnabled();
+        return cached.isDirectAccessGrantsEnabled();
+    }
+
+    @Override
+    public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
+        getDelegateForUpdate();
+        updated.setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
+    }
+
+    @Override
+    public boolean isServiceAccountsEnabled() {
+        if (updated != null) return updated.isServiceAccountsEnabled();
+        return cached.isServiceAccountsEnabled();
+    }
+
+    @Override
+    public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+        getDelegateForUpdate();
+        updated.setServiceAccountsEnabled(serviceAccountsEnabled);
+    }
+
+
 
 
     @Override
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java
index 58bcc12..b03430d 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java
@@ -29,8 +29,17 @@ public class CachedClientTemplate implements Serializable {
     private String realm;
     private String protocol;
     private boolean fullScopeAllowed;
+    private boolean publicClient;
+    private boolean frontchannelLogout;
+    private boolean bearerOnly;
+    private boolean consentRequired;
+    private boolean standardFlowEnabled;
+    private boolean implicitFlowEnabled;
+    private boolean directAccessGrantsEnabled;
+    private boolean serviceAccountsEnabled;
     private Set<String> scope = new HashSet<String>();
     private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
+    private Map<String, String> attributes = new HashMap<String, String>();
 
     public CachedClientTemplate(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) {
         id = model.getId();
@@ -45,6 +54,15 @@ public class CachedClientTemplate implements Serializable {
         for (RoleModel role : model.getScopeMappings())  {
             scope.add(role.getId());
         }
+        attributes.putAll(model.getAttributes());
+        frontchannelLogout = model.isFrontchannelLogout();
+        publicClient = model.isPublicClient();
+        bearerOnly = model.isBearerOnly();
+        consentRequired = model.isConsentRequired();
+        standardFlowEnabled = model.isStandardFlowEnabled();
+        implicitFlowEnabled = model.isImplicitFlowEnabled();
+        directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled();
+        serviceAccountsEnabled = model.isServiceAccountsEnabled();
     }
     public String getId() {
         return id;
@@ -77,4 +95,40 @@ public class CachedClientTemplate implements Serializable {
     public Set<String> getScope() {
         return scope;
     }
+
+    public boolean isPublicClient() {
+        return publicClient;
+    }
+
+    public boolean isFrontchannelLogout() {
+        return frontchannelLogout;
+    }
+
+    public boolean isBearerOnly() {
+        return bearerOnly;
+    }
+
+    public boolean isConsentRequired() {
+        return consentRequired;
+    }
+
+    public boolean isStandardFlowEnabled() {
+        return standardFlowEnabled;
+    }
+
+    public boolean isImplicitFlowEnabled() {
+        return implicitFlowEnabled;
+    }
+
+    public boolean isDirectAccessGrantsEnabled() {
+        return directAccessGrantsEnabled;
+    }
+
+    public boolean isServiceAccountsEnabled() {
+        return serviceAccountsEnabled;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java
index 2d5c78b..4e70deb 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java
@@ -288,6 +288,111 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
     }
 
     @Override
+    public boolean isPublicClient() {
+        return entity.isPublicClient();
+    }
+
+    @Override
+    public void setPublicClient(boolean flag) {
+        entity.setPublicClient(flag);
+    }
+
+    @Override
+    public boolean isFrontchannelLogout() {
+        return entity.isFrontchannelLogout();
+    }
+
+    @Override
+    public void setFrontchannelLogout(boolean flag) {
+        entity.setFrontchannelLogout(flag);
+    }
+
+    @Override
+    public void setAttribute(String name, String value) {
+        entity.getAttributes().put(name, value);
+
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        entity.getAttributes().remove(name);
+    }
+
+    @Override
+    public String getAttribute(String name) {
+        return entity.getAttributes().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        Map<String, String> copy = new HashMap<>();
+        copy.putAll(entity.getAttributes());
+        return copy;
+    }
+
+    @Override
+    public boolean isBearerOnly() {
+        return entity.isBearerOnly();
+    }
+
+    @Override
+    public void setBearerOnly(boolean only) {
+        entity.setBearerOnly(only);
+    }
+
+    @Override
+    public boolean isConsentRequired() {
+        return entity.isConsentRequired();
+    }
+
+    @Override
+    public void setConsentRequired(boolean consentRequired) {
+        entity.setConsentRequired(consentRequired);
+    }
+
+    @Override
+    public boolean isStandardFlowEnabled() {
+        return entity.isStandardFlowEnabled();
+    }
+
+    @Override
+    public void setStandardFlowEnabled(boolean standardFlowEnabled) {
+        entity.setStandardFlowEnabled(standardFlowEnabled);
+    }
+
+    @Override
+    public boolean isImplicitFlowEnabled() {
+        return entity.isImplicitFlowEnabled();
+    }
+
+    @Override
+    public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
+        entity.setImplicitFlowEnabled(implicitFlowEnabled);
+    }
+
+    @Override
+    public boolean isDirectAccessGrantsEnabled() {
+        return entity.isDirectAccessGrantsEnabled();
+    }
+
+    @Override
+    public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
+        entity.setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
+    }
+
+    @Override
+    public boolean isServiceAccountsEnabled() {
+        return entity.isServiceAccountsEnabled();
+    }
+
+    @Override
+    public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+        entity.setServiceAccountsEnabled(serviceAccountsEnabled);
+    }
+
+
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || !(o instanceof ClientTemplateModel)) return false;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
index 5da01c4..ba3fc62 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
@@ -48,6 +48,35 @@ public class ClientTemplateEntity {
     @Column(name="FULL_SCOPE_ALLOWED")
     private boolean fullScopeAllowed;
 
+    @Column(name="CONSENT_REQUIRED")
+    private boolean consentRequired;
+
+    @Column(name="STANDARD_FLOW_ENABLED")
+    private boolean standardFlowEnabled;
+
+    @Column(name="IMPLICIT_FLOW_ENABLED")
+    private boolean implicitFlowEnabled;
+
+    @Column(name="DIRECT_ACCESS_GRANTS_ENABLED")
+    private boolean directAccessGrantsEnabled;
+
+    @Column(name="SERVICE_ACCOUNTS_ENABLED")
+    private boolean serviceAccountsEnabled;
+
+    @Column(name="FRONTCHANNEL_LOGOUT")
+    private boolean frontchannelLogout;
+    @Column(name="PUBLIC_CLIENT")
+    private boolean publicClient;
+    @Column(name="BEARER_ONLY")
+    private boolean bearerOnly;
+
+
+    @ElementCollection
+    @MapKeyColumn(name="NAME")
+    @Column(name="VALUE", length = 2048)
+    @CollectionTable(name="CLIENT_TEMPLATE_ATTRIBUTES", joinColumns={ @JoinColumn(name="TEMPLATE_ID") })
+    protected Map<String, String> attributes = new HashMap<String, String>();
+
     public RealmEntity getRealm() {
         return realm;
     }
@@ -103,4 +132,76 @@ public class ClientTemplateEntity {
     public void setFullScopeAllowed(boolean fullScopeAllowed) {
         this.fullScopeAllowed = fullScopeAllowed;
     }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public boolean isConsentRequired() {
+        return consentRequired;
+    }
+
+    public void setConsentRequired(boolean consentRequired) {
+        this.consentRequired = consentRequired;
+    }
+
+    public boolean isStandardFlowEnabled() {
+        return standardFlowEnabled;
+    }
+
+    public void setStandardFlowEnabled(boolean standardFlowEnabled) {
+        this.standardFlowEnabled = standardFlowEnabled;
+    }
+
+    public boolean isImplicitFlowEnabled() {
+        return implicitFlowEnabled;
+    }
+
+    public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
+        this.implicitFlowEnabled = implicitFlowEnabled;
+    }
+
+    public boolean isDirectAccessGrantsEnabled() {
+        return directAccessGrantsEnabled;
+    }
+
+    public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
+        this.directAccessGrantsEnabled = directAccessGrantsEnabled;
+    }
+
+    public boolean isServiceAccountsEnabled() {
+        return serviceAccountsEnabled;
+    }
+
+    public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+        this.serviceAccountsEnabled = serviceAccountsEnabled;
+    }
+
+    public boolean isFrontchannelLogout() {
+        return frontchannelLogout;
+    }
+
+    public void setFrontchannelLogout(boolean frontchannelLogout) {
+        this.frontchannelLogout = frontchannelLogout;
+    }
+
+    public boolean isPublicClient() {
+        return publicClient;
+    }
+
+    public void setPublicClient(boolean publicClient) {
+        this.publicClient = publicClient;
+    }
+
+    public boolean isBearerOnly() {
+        return bearerOnly;
+    }
+
+    public void setBearerOnly(boolean bearerOnly) {
+        this.bearerOnly = bearerOnly;
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java
index 91725bf..9b2d921 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java
@@ -275,6 +275,121 @@ public class ClientTemplateAdapter extends AbstractMongoAdapter<MongoClientTempl
     }
 
     @Override
+    public boolean isPublicClient() {
+        return getMongoEntity().isPublicClient();
+    }
+
+    @Override
+    public void setPublicClient(boolean flag) {
+        getMongoEntity().setPublicClient(flag);
+        updateMongoEntity();
+    }
+
+
+    @Override
+    public boolean isFrontchannelLogout() {
+        return getMongoEntity().isFrontchannelLogout();
+    }
+
+    @Override
+    public void setFrontchannelLogout(boolean flag) {
+        getMongoEntity().setFrontchannelLogout(flag);
+        updateMongoEntity();
+    }
+
+    @Override
+    public void setAttribute(String name, String value) {
+        getMongoEntity().getAttributes().put(name, value);
+        updateMongoEntity();
+
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        getMongoEntity().getAttributes().remove(name);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getAttribute(String name) {
+        return getMongoEntity().getAttributes().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        Map<String, String> copy = new HashMap<String, String>();
+        copy.putAll(getMongoEntity().getAttributes());
+        return copy;
+    }
+
+    @Override
+    public boolean isBearerOnly() {
+        return getMongoEntity().isBearerOnly();
+    }
+
+    @Override
+    public void setBearerOnly(boolean only) {
+        getMongoEntity().setBearerOnly(only);
+        updateMongoEntity();
+    }
+
+    @Override
+    public boolean isConsentRequired() {
+        return getMongoEntity().isConsentRequired();
+    }
+
+    @Override
+    public void setConsentRequired(boolean consentRequired) {
+        getMongoEntity().setConsentRequired(consentRequired);
+        updateMongoEntity();
+    }
+
+    @Override
+    public boolean isStandardFlowEnabled() {
+        return getMongoEntity().isStandardFlowEnabled();
+    }
+
+    @Override
+    public void setStandardFlowEnabled(boolean standardFlowEnabled) {
+        getMongoEntity().setStandardFlowEnabled(standardFlowEnabled);
+        updateMongoEntity();
+    }
+
+    @Override
+    public boolean isImplicitFlowEnabled() {
+        return getMongoEntity().isImplicitFlowEnabled();
+    }
+
+    @Override
+    public void setImplicitFlowEnabled(boolean implicitFlowEnabled) {
+        getMongoEntity().setImplicitFlowEnabled(implicitFlowEnabled);
+        updateMongoEntity();
+    }
+
+    @Override
+    public boolean isDirectAccessGrantsEnabled() {
+        return getMongoEntity().isDirectAccessGrantsEnabled();
+    }
+
+    @Override
+    public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) {
+        getMongoEntity().setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
+        updateMongoEntity();
+    }
+
+    @Override
+    public boolean isServiceAccountsEnabled() {
+        return getMongoEntity().isServiceAccountsEnabled();
+    }
+
+    @Override
+    public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+        getMongoEntity().setServiceAccountsEnabled(serviceAccountsEnabled);
+        updateMongoEntity();
+    }
+
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || !(o instanceof ClientTemplateModel)) return false;
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
index 42ff3ee..20f7810 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
@@ -4,10 +4,8 @@ import org.keycloak.Config;
 import org.keycloak.dom.saml.v2.metadata.*;
 import org.keycloak.exportimport.ClientDescriptionConverter;
 import org.keycloak.exportimport.ClientDescriptionConverterFactory;
-import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.saml.SignatureAlgorithm;
@@ -80,12 +78,12 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
 
         app.setFullScopeAllowed(true);
         app.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
-        attributes.put(SamlProtocol.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
-        attributes.put(SamlProtocol.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
-        attributes.put(SamlProtocol.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+        attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
+        attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
+        attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
         SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity);
         if (spDescriptorType.isWantAssertionsSigned()) {
-            attributes.put(SamlProtocol.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+            attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
         }
         String logoutPost = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
         if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, logoutPost);
@@ -114,10 +112,10 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
             }
             String certPem = KeycloakModelUtils.getPemFromCertificate(cert);
             if (keyDescriptor.getUse() == KeyTypes.SIGNING) {
-                attributes.put(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
-                attributes.put(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
+                attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+                attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
             } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
-                attributes.put(SamlProtocol.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+                attributes.put(SamlConfigAttributes.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
                 attributes.put(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
             }
         }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
old mode 100644
new mode 100755
index 52b8557..f262346
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
@@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.DefaultAuthenticationFlows;
 import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
+import org.keycloak.protocol.saml.SamlConfigAttributes;
 import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.protocol.saml.SamlService;
 import org.keycloak.protocol.saml.profile.ecp.util.Soap;
@@ -99,7 +100,7 @@ public class SamlEcpProfileService extends SamlService {
             private void createRequestAuthenticatedHeader(ClientSessionModel clientSession, Soap.SoapMessageBuilder messageBuilder) {
                 ClientModel client = clientSession.getClient();
 
-                if ("true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
+                if ("true".equals(client.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
                     SOAPHeaderElement ecpRequestAuthenticated = messageBuilder.addHeader(JBossSAMLConstants.REQUEST_AUTHENTICATED.get(), NS_PREFIX_PROFILE_ECP);
 
                     ecpRequestAuthenticated.setMustUnderstand(true);
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
index 241cc26..d935f83 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
@@ -1,5 +1,6 @@
 package org.keycloak.protocol.saml;
 
+import org.keycloak.models.ClientConfigResolver;
 import org.keycloak.models.ClientModel;
 import org.keycloak.saml.SignatureAlgorithm;
 
@@ -7,24 +8,22 @@ import org.keycloak.saml.SignatureAlgorithm;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class SamlClient {
-    public static final String SAML_SIGNING_PRIVATE_KEY = "saml.signing.private.key";
-    protected ClientModel client;
+public class SamlClient extends ClientConfigResolver {
 
     public SamlClient(ClientModel client) {
-        this.client = client;
+        super(client);
     }
 
     public String getCanonicalizationMethod() {
-        return client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+        return resolveAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
     }
 
     public void setCanonicalizationMethod(String value) {
-        client.setAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value);
+        client.setAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value);
     }
 
     public SignatureAlgorithm getSignatureAlgorithm() {
-        String alg = client.getAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM);
+        String alg = resolveAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM);
         if (alg != null) {
             SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
             if (algorithm != null)
@@ -34,94 +33,92 @@ public class SamlClient {
     }
 
     public void setSignatureAlgorithm(SignatureAlgorithm algorithm) {
-        client.setAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM, algorithm.name());
+        client.setAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, algorithm.name());
     }
 
     public String getNameIDFormat() {
-        return client.getAttributes().get(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE);
+        return resolveAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE);
     }
     public void setNameIDFormat(String format) {
-        client.setAttribute(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE, format);
+        client.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format);
     }
 
     public boolean includeAuthnStatement() {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_AUTHNSTATEMENT));
+        return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT));
     }
 
     public void setIncludeAuthnStatement(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_AUTHNSTATEMENT, Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, Boolean.toString(val));
     }
 
     public boolean forceNameIDFormat() {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
+        return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
 
     }
     public void setForceNameIDFormat(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val));
     }
 
-    public boolean requiresRealmSignature(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_SERVER_SIGNATURE));
+    public boolean requiresRealmSignature() {
+        return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE));
     }
 
     public void setRequiresRealmSignature(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_SERVER_SIGNATURE, Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
 
     }
 
-    public boolean forcePostBinding(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING));
+    public boolean forcePostBinding() {
+        return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING));
     }
 
     public void setForcePostBinding(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_FORCE_POST_BINDING, Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING, Boolean.toString(val));
 
     }
-    public boolean samlAssertionSignature(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE));
+    public boolean requiresAssertionSignature() {
+       return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE));
     }
 
-    public void setAssertionSignature(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE   , Boolean.toString(val));
+    public void setRequiresAssertionSignature(boolean val) {
+        client.setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE   , Boolean.toString(val));
 
     }
-    public boolean requiresEncryption(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_ENCRYPT));
+    public boolean requiresEncryption() {
+       return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_ENCRYPT));
     }
 
 
     public void setRequiresEncryption(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_ENCRYPT, Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(val));
 
     }
 
-    public boolean requiresClientSignature(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE));
+    public boolean requiresClientSignature() {
+        return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE));
     }
 
     public void setRequiresClientSignature(boolean val) {
-        client.setAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE   , Boolean.toString(val));
+        client.setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE   , Boolean.toString(val));
 
     }
 
     public String getClientSigningCertificate() {
-        return client.getAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+        return client.getAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
     }
 
     public void setClientSigningCertificate(String val) {
-        client.setAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val);
+        client.setAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val);
 
     }
 
     public String getClientSigningPrivateKey() {
-        return client.getAttribute(SAML_SIGNING_PRIVATE_KEY);
+        return client.getAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY);
     }
 
     public void setClientSigningPrivateKey(String val) {
-        client.setAttribute(SAML_SIGNING_PRIVATE_KEY, val);
+        client.setAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY, val);
 
     }
 
-
-
 }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java
new file mode 100755
index 0000000..ae6d491
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java
@@ -0,0 +1,131 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.saml.SignatureAlgorithm;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlClientTemplate {
+    protected ClientTemplateModel clientTemplate;
+
+    public SamlClientTemplate(ClientTemplateModel template) {
+        this.clientTemplate = template;
+    }
+
+    public String getId() {
+        return clientTemplate.getId();
+    }
+
+//
+
+    public String getCanonicalizationMethod() {
+        return clientTemplate.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+    }
+
+    public void setCanonicalizationMethod(String value) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value);
+    }
+
+    public SignatureAlgorithm getSignatureAlgorithm() {
+        String alg = null;
+        alg = clientTemplate.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+        if (alg != null) {
+            SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
+            if (algorithm != null)
+                return algorithm;
+        }
+        return SignatureAlgorithm.RSA_SHA256;
+    }
+
+    public void setSignatureAlgorithm(SignatureAlgorithm algorithm) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, algorithm.name());
+    }
+
+    public String getNameIDFormat() {
+        return clientTemplate.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE);
+    }
+    public void setNameIDFormat(String format) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format);
+    }
+
+    public boolean includeAuthnStatement() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT));
+    }
+
+    public void setIncludeAuthnStatement(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, Boolean.toString(val));
+    }
+
+    public boolean forceNameIDFormat() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
+
+    }
+    public void setForceNameIDFormat(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val));
+    }
+
+    public boolean requiresRealmSignature() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE));
+    }
+
+    public void setRequiresRealmSignature(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
+
+    }
+
+    public boolean forcePostBinding() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING));
+    }
+
+    public void setForcePostBinding(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING, Boolean.toString(val));
+
+    }
+    public boolean requiresAssertionSignature() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE));
+    }
+
+    public void setRequiresAssertionSignature(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE   , Boolean.toString(val));
+
+    }
+    public boolean requiresEncryption() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_ENCRYPT));
+    }
+
+
+    public void setRequiresEncryption(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(val));
+
+    }
+
+    public boolean requiresClientSignature() {
+        return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE));
+    }
+
+    public void setRequiresClientSignature(boolean val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE   , Boolean.toString(val));
+
+    }
+
+    public String getClientSigningCertificate() {
+        return clientTemplate.getAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+    }
+
+    public void setClientSigningCertificate(String val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val);
+
+    }
+
+    public String getClientSigningPrivateKey() {
+        return clientTemplate.getAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY);
+    }
+
+    public void setClientSigningPrivateKey(String val) {
+        clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY, val);
+
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
new file mode 100755
index 0000000..eea258a
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
@@ -0,0 +1,22 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface SamlConfigAttributes {
+    String SAML_SIGNING_PRIVATE_KEY = "saml.signing.private.key";
+    String SAML_CANONICALIZATION_METHOD_ATTRIBUTE = "saml_signature_canonicalization_method";
+    String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
+    String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format";
+    String SAML_AUTHNSTATEMENT = "saml.authnstatement";
+    String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
+    String SAML_SERVER_SIGNATURE = "saml.server.signature";
+    String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
+    String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
+    String SAML_ENCRYPT = "saml.encrypt";
+    String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
+    String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 07c528d..e7bd3eb 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -70,28 +70,17 @@ public class SamlProtocol implements LoginProtocol {
 
     public static final String ATTRIBUTE_TRUE_VALUE = "true";
     public static final String ATTRIBUTE_FALSE_VALUE = "false";
-    public static final String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
     public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
-    public static final String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
     public static final String SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE = "saml_assertion_consumer_url_post";
     public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect";
     public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post";
     public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE = "saml_single_logout_service_url_redirect";
-    public static final String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
-    public static final String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format";
-    public static final String SAML_CANONICALIZATION_METHOD_ATTRIBUTE = "saml_signature_canonicalization_method";
     public static final String LOGIN_PROTOCOL = "saml";
     public static final String SAML_BINDING = "saml_binding";
     public static final String SAML_IDP_INITIATED_LOGIN = "saml_idp_initiated_login";
     public static final String SAML_POST_BINDING = "post";
     public static final String SAML_SOAP_BINDING = "soap";
     public static final String SAML_REDIRECT_BINDING = "get";
-    public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
-    public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
-    public static final String SAML_AUTHNSTATEMENT = "saml.authnstatement";
-    public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
-    public static final String SAML_ENCRYPT = "saml.encrypt";
-    public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
     public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
     public static final String SAML_LOGOUT_BINDING = "saml.logout.binding";
     public static final String SAML_LOGOUT_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID";
@@ -218,7 +207,8 @@ public class SamlProtocol implements LoginProtocol {
 
     protected boolean isPostBinding(ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
-        return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || forcePostBinding(client);
+        SamlClient samlClient = new SamlClient(client);
+        return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || samlClient.forcePostBinding();
     }
 
     public static boolean isLogoutPostBindingForInitiator(UserSessionModel session) {
@@ -228,6 +218,7 @@ public class SamlProtocol implements LoginProtocol {
 
     protected boolean isLogoutPostBindingForClient(ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
+        SamlClient samlClient = new SamlClient(client);
         String logoutPostUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
         String logoutRedirectUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE);
 
@@ -238,7 +229,7 @@ public class SamlProtocol implements LoginProtocol {
             return false;
         }
 
-        if (forcePostBinding(client)) {
+        if (samlClient.forcePostBinding()) {
             return true; // configured to force a post binding and post binding logout url is not null
         }
 
@@ -255,15 +246,11 @@ public class SamlProtocol implements LoginProtocol {
 
     }
 
-    public static boolean forcePostBinding(ClientModel client) {
-        return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING));
-    }
-
-    protected String getNameIdFormat(ClientSessionModel clientSession) {
+    protected String getNameIdFormat(SamlClient samlClient, ClientSessionModel clientSession) {
         String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT);
-        ClientModel client = clientSession.getClient();
-        boolean forceFormat = forceNameIdFormat(client);
-        String configuredNameIdFormat = client.getAttribute(SAML_NAME_ID_FORMAT_ATTRIBUTE);
+
+        boolean forceFormat = samlClient.forceNameIDFormat();
+        String configuredNameIdFormat = samlClient.getNameIDFormat();
         if ((nameIdFormat == null || forceFormat) && configuredNameIdFormat != null) {
             if (configuredNameIdFormat.equals("email")) {
                 nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get();
@@ -282,11 +269,7 @@ public class SamlProtocol implements LoginProtocol {
         return nameIdFormat;
     }
 
-    public static boolean forceNameIdFormat(ClientModel client) {
-        return "true".equals(client.getAttribute(SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
-    }
-
-    protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
+     protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
         if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
             return userSession.getUser().getEmail();
         } else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) {
@@ -315,11 +298,12 @@ public class SamlProtocol implements LoginProtocol {
     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
         ClientSessionModel clientSession = accessCode.getClientSession();
         ClientModel client = clientSession.getClient();
+        SamlClient samlClient = new SamlClient(client);
         String requestID = clientSession.getNote(SAML_REQUEST_ID);
         String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
         String redirectUri = clientSession.getRedirectUri();
         String responseIssuer = getResponseIssuer(realm);
-        String nameIdFormat = getNameIdFormat(clientSession);
+        String nameIdFormat = getNameIdFormat(samlClient, clientSession);
         String nameId = getNameId(nameIdFormat, clientSession, userSession);
 
         // save NAME_ID and format in clientSession as they may be persistent or transient or email and not username
@@ -330,7 +314,7 @@ public class SamlProtocol implements LoginProtocol {
         SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder();
         builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(realm.getAccessCodeLifespan()).subjectExpiration(realm.getAccessTokenLifespan()).sessionIndex(clientSession.getId())
                 .requestIssuer(clientSession.getClient().getClientId()).nameIdentifier(nameIdFormat, nameId).authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
-        if (!includeAuthnStatement(client)) {
+        if (!samlClient.includeAuthnStatement()) {
             builder.disableAuthnStatement(true);
         }
 
@@ -370,21 +354,21 @@ public class SamlProtocol implements LoginProtocol {
         JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
         bindingBuilder.relayState(relayState);
 
-        if (requiresRealmSignature(client)) {
-            String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+        if (samlClient.requiresRealmSignature()) {
+            String canonicalization = samlClient.getCanonicalizationMethod();
             if (canonicalization != null) {
                 bindingBuilder.canonicalizationMethod(canonicalization);
             }
-            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
+            bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
         }
-        if (requiresAssertionSignature(client)) {
-            String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+        if (samlClient.requiresAssertionSignature()) {
+            String canonicalization = samlClient.getCanonicalizationMethod();
             if (canonicalization != null) {
                 bindingBuilder.canonicalizationMethod(canonicalization);
             }
-            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signAssertions();
+            bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signAssertions();
         }
-        if (requiresEncryption(client)) {
+        if (samlClient.requiresEncryption()) {
             PublicKey publicKey = null;
             try {
                 publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
@@ -410,32 +394,6 @@ public class SamlProtocol implements LoginProtocol {
         }
     }
 
-    public static boolean requiresRealmSignature(ClientModel client) {
-        return "true".equals(client.getAttribute(SAML_SERVER_SIGNATURE));
-    }
-
-    public static boolean requiresAssertionSignature(ClientModel client) {
-        return "true".equals(client.getAttribute(SAML_ASSERTION_SIGNATURE));
-    }
-
-    public static boolean includeAuthnStatement(ClientModel client) {
-        return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
-    }
-
-    public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
-        String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
-        if (alg != null) {
-            SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
-            if (algorithm != null)
-                return algorithm;
-        }
-        return SignatureAlgorithm.RSA_SHA256;
-    }
-
-    private boolean requiresEncryption(ClientModel client) {
-        return "true".equals(client.getAttribute(SAML_ENCRYPT));
-    }
-
     public static class ProtocolMapperProcessor<T> {
         final public T mapper;
         final public ProtocolMapperModel model;
@@ -499,19 +457,20 @@ public class SamlProtocol implements LoginProtocol {
     @Override
     public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
+        SamlClient samlClient = new SamlClient(client);
         if (!(client instanceof ClientModel))
             return null;
         try {
             if (isLogoutPostBindingForClient(clientSession)) {
                 String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
                 SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
-                JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+                JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
                 return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
             } else {
                 logger.debug("frontchannel redirect binding");
                 String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
                 SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
-                JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+                JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
                 return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri);
             }
         } catch (ConfigurationException e) {
@@ -574,6 +533,7 @@ public class SamlProtocol implements LoginProtocol {
     @Override
     public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
+        SamlClient samlClient = new SamlClient(client);
         String logoutUrl = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
         if (logoutUrl == null) {
             logger.warnv("Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: {1}", client.getClientId());
@@ -583,7 +543,7 @@ public class SamlProtocol implements LoginProtocol {
 
         String logoutRequestString = null;
         try {
-            JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
+            JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
             logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded();
         } catch (Exception e) {
             logger.warn("failed to send saml logout", e);
@@ -636,10 +596,10 @@ public class SamlProtocol implements LoginProtocol {
         return logoutBuilder;
     }
 
-    private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) {
+    private JaxrsSAML2BindingBuilder createBindingBuilder(SamlClient samlClient) {
         JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
-        if (requiresRealmSignature(client)) {
-            binding.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
+        if (samlClient.requiresRealmSignature()) {
+            binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
         }
         return binding;
     }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
index f7b4d1e..1de9f45 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
@@ -3,6 +3,7 @@ package org.keycloak.protocol.saml;
 import org.keycloak.Config;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
@@ -14,6 +15,7 @@ import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.saml.SignatureAlgorithm;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
@@ -103,7 +105,7 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
 
     @Override
     public void setupClientDefaults(ClientRepresentation clientRep, ClientModel newClient) {
-        SamlClientRepresentation rep = new SamlClientRepresentation(clientRep);
+        SamlRepresentationAttributes rep = new SamlRepresentationAttributes(clientRep.getAttributes());
         SamlClient client = new SamlClient(newClient);
         if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true);
         if (rep.getCanonicalizationMethod() == null) {
@@ -134,9 +136,53 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
 
         if (rep.getClientSignature() == null) {
             client.setRequiresClientSignature(true);
+        }
+
+        if (client.requiresClientSignature() && client.getClientSigningCertificate() == null) {
             CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(newClient.getClientId());
             client.setClientSigningCertificate(info.getCertificate());
             client.setClientSigningPrivateKey(info.getPrivateKey());
+
+        }
+
+        if (clientRep.isFrontchannelLogout() == null) {
+            newClient.setFrontchannelLogout(true);
+        }
+    }
+
+    @Override
+    public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) {
+        SamlRepresentationAttributes rep = new SamlRepresentationAttributes(clientRep.getAttributes());
+        SamlClientTemplate client = new SamlClientTemplate(newClient);
+        if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true);
+        if (rep.getCanonicalizationMethod() == null) {
+            client.setCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE);
+        }
+        if (rep.getSignatureAlgorithm() == null) {
+            client.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
+        }
+
+        if (rep.getNameIDFormat() == null) {
+            client.setNameIDFormat("username");
+        }
+
+        if (rep.getIncludeAuthnStatement() == null) {
+            client.setIncludeAuthnStatement(true);
+        }
+
+        if (rep.getForceNameIDFormat() == null) {
+            client.setForceNameIDFormat(false);
+        }
+
+        if (rep.getSamlServerSignature() == null) {
+            client.setRequiresRealmSignature(true);
+        }
+        if (rep.getForcePostBinding() == null) {
+            client.setForcePostBinding(true);
+        }
+
+        if (rep.getClientSignature() == null) {
+            client.setRequiresClientSignature(true);
         }
 
         if (clientRep.isFrontchannelLogout() == null) {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
index d625f2a..5742f7d 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -25,7 +25,8 @@ public class SamlProtocolUtils {
 
 
     public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
-        if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
+        SamlClient samlClient = new SamlClient(client);
+        if (!samlClient.requiresClientSignature()) {
             return;
         }
         PublicKey publicKey = getSignatureValidationKey(client);
@@ -44,7 +45,7 @@ public class SamlProtocolUtils {
     }
 
     public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException {
-        return getPublicKey(client, SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+        return getPublicKey(new SamlClient(client).getClientSigningCertificate());
     }
 
     public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
@@ -53,6 +54,10 @@ public class SamlProtocolUtils {
 
     public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException {
         String certPem = client.getAttribute(attribute);
+        return getPublicKey(certPem);
+    }
+
+    private static PublicKey getPublicKey(String certPem) throws VerificationException {
         if (certPem == null) throw new VerificationException("Client does not have a public key.");
         Certificate cert = null;
         try {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 67edd49..bd6e846 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -192,6 +192,7 @@ public class SamlService extends AuthorizationEndpointBase {
         protected abstract SAMLDocumentHolder extractResponseDocument(String response);
 
         protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType, ClientModel client) {
+            SamlClient samlClient = new SamlClient(client);
             // validate destination
             if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
                 event.detail(Details.REASON, "invalid_destination");
@@ -199,7 +200,7 @@ public class SamlService extends AuthorizationEndpointBase {
                 return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
             String bindingType = getBindingType(requestAbstractType);
-            if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING)))
+            if (samlClient.forcePostBinding())
                 bindingType = SamlProtocol.SAML_POST_BINDING;
             String redirect = null;
             URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL();
@@ -233,7 +234,7 @@ public class SamlService extends AuthorizationEndpointBase {
 
             // Handle NameIDPolicy from SP
             NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy();
-            if (nameIdPolicy != null && !SamlProtocol.forceNameIdFormat(client)) {
+            if (nameIdPolicy != null && !samlClient.forceNameIDFormat()) {
                 String nameIdFormat = nameIdPolicy.getFormat().toString();
                 // TODO: Handle AllowCreate too, relevant for persistent NameID.
                 if (isSupportedNameIdFormat(nameIdFormat)) {
@@ -273,6 +274,7 @@ public class SamlService extends AuthorizationEndpointBase {
         protected abstract String getBindingType();
 
         protected Response logoutRequest(LogoutRequestType logoutRequest, ClientModel client, String relayState) {
+            SamlClient samlClient = new SamlClient(client);
             // validate destination
             if (logoutRequest.getDestination() != null && !uriInfo.getAbsolutePath().equals(logoutRequest.getDestination())) {
                 event.detail(Details.REASON, "invalid_destination");
@@ -284,20 +286,20 @@ public class SamlService extends AuthorizationEndpointBase {
             AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false);
             if (authResult != null) {
                 String logoutBinding = getBindingType();
-                if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING)))
+                if ("true".equals(samlClient.forcePostBinding()))
                     logoutBinding = SamlProtocol.SAML_POST_BINDING;
                 String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding);
                 UserSessionModel userSession = authResult.getSession();
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri);
-                if (SamlProtocol.requiresRealmSignature(client)) {
-                    userSession.setNote(SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, SamlProtocol.getSignatureAlgorithm(client).toString());
+                if (samlClient.requiresRealmSignature()) {
+                    userSession.setNote(SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, samlClient.getSignatureAlgorithm().toString());
 
                 }
                 if (relayState != null)
                     userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
-                userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE));
+                userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod());
                 userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
                 // remove client from logout requests
                 for (ClientSessionModel clientSession : userSession.getClientSessions()) {
@@ -347,8 +349,8 @@ public class SamlService extends AuthorizationEndpointBase {
             builder.destination(logoutBindingUri);
             builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
             JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState);
-            if (SamlProtocol.requiresRealmSignature(client)) {
-                SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client);
+            if (samlClient.requiresRealmSignature()) {
+                SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm();
                 binding.signatureAlgorithm(algorithm).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
 
             }
@@ -410,7 +412,8 @@ public class SamlService extends AuthorizationEndpointBase {
 
         @Override
         protected void verifySignature(SAMLDocumentHolder documentHolder, ClientModel client) throws VerificationException {
-            if (!"true".equals(client.getAttribute("saml.client.signature"))) {
+            SamlClient samlClient = new SamlClient(client);
+            if (!samlClient.requiresClientSignature()) {
                 return;
             }
             PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
index a876b19..2238e6f 100755
--- a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
@@ -2,10 +2,12 @@ package org.keycloak.protocol;
 
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 
 import java.util.List;
@@ -31,10 +33,18 @@ public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
     Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager);
 
     /**
-     * Setup default values for new clients.
+     * Setup default values for new clients. This expects that the representation has already set up the client
      *
      * @param rep
      * @param newClient
      */
     void setupClientDefaults(ClientRepresentation rep, ClientModel newClient);
+
+    /**
+     * Setup default values for new templates.  This expects that the representation has already set up the template
+     *
+     * @param clientRep
+     * @param newClient
+     */
+    void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient);
 }
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 8cd0d8f..ee710ce 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -21,6 +21,7 @@ import org.keycloak.common.constants.KerberosConstants;
 import org.keycloak.common.util.UriUtils;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
@@ -32,6 +33,7 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
 import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 
 import java.util.ArrayList;
@@ -206,4 +208,9 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
         if (rep.isPublicClient() == null) newClient.setPublicClient(true);
         if (rep.isFrontchannelLogout() == null) newClient.setFrontchannelLogout(false);
     }
+
+    @Override
+    public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) {
+
+    }
 }