keycloak-aplcache

template scope

12/18/2015 8:15:27 PM

Changes

Details

diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 36f33bf..b2af448 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -36,6 +36,7 @@
         <class>org.keycloak.models.jpa.entities.GroupRoleMappingEntity</class>
         <class>org.keycloak.models.jpa.entities.UserGroupMembershipEntity</class>
         <class>org.keycloak.models.jpa.entities.ClientTemplateEntity</class>
+        <class>org.keycloak.models.jpa.entities.TemplateScopeMappingEntity</class>
 
         <!-- JpaAuditProviders -->
         <class>org.keycloak.events.jpa.EventEntity</class>
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 db2a791..068d4e7 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
@@ -15,6 +15,17 @@
             <column name="REALM_ID" type="VARCHAR(36)"/>
             <column name="DESCRIPTION" type="VARCHAR(255)"/>
             <column name="PROTOCOL" type="VARCHAR(255)"/>
+            <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="TEMPLATE_SCOPE_MAPPING">
+            <column name="TEMPLATE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ROLE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
         </createTable>
 
 
@@ -24,12 +35,21 @@
             <column name="CLIENT_TEMPLATE_ID" type="VARCHAR(36)">
                 <constraints nullable="true"/>
             </column>
+            <column name="USE_TEMPLATE_CONFIG" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USE_TEMPLATE_SCOPE" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USE_TEMPLATE_MAPPERS" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
         </addColumn>
         <addColumn tableName="PROTOCOL_MAPPER">
             <column name="CLIENT_TEMPLATE_ID" type="VARCHAR(36)">
                 <constraints nullable="true"/>
             </column>
-        </addColumn>
+         </addColumn>
         <createTable tableName="REALM_CLIENT_TEMPLATE">
             <column name="CLIENT_TEMPLATE_ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
@@ -46,6 +66,9 @@
         <addForeignKeyConstraint baseColumnNames="CLIENT_TEMPLATE_ID" baseTableName="CLIENT" constraintName="FK_CLI_TMPLT_CLIENT" referencedColumnNames="ID" referencedTableName="CLIENT_TEMPLATE"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_CLIENT_TEMPLATE" constraintName="FK_RLM_CLI_TMPLT_RLM" referencedColumnNames="ID" referencedTableName="REALM"/>
         <addForeignKeyConstraint baseColumnNames="CLIENT_TEMPLATE_ID" baseTableName="REALM_CLIENT_TEMPLATE" constraintName="FK_RLM_CLI_TMPLT_CLI" referencedColumnNames="ID" referencedTableName="CLIENT_TEMPLATE"/>
+        <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"/>
 
 
     </changeSet>
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index 2384a2e..ab81db5 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -41,6 +41,10 @@ public class ClientRepresentation {
     protected Map<String, Integer> registeredNodes;
     protected List<ProtocolMapperRepresentation> protocolMappers;
     protected String clientTemplate;
+    private Boolean useTemplateConfig;
+    private Boolean useTemplateScope;
+    private Boolean useTemplateMappers;
+
 
     public String getId() {
         return id;
@@ -298,4 +302,29 @@ public class ClientRepresentation {
     public void setClientTemplate(String clientTemplate) {
         this.clientTemplate = clientTemplate;
     }
+
+    public Boolean isUseTemplateConfig() {
+        return useTemplateConfig;
+    }
+
+    public void setUseTemplateConfig(Boolean useTemplateConfig) {
+        this.useTemplateConfig = useTemplateConfig;
+    }
+
+    public Boolean isUseTemplateScope() {
+        return useTemplateScope;
+    }
+
+    public void setUseTemplateScope(Boolean useTemplateScope) {
+        this.useTemplateScope = useTemplateScope;
+    }
+
+    public Boolean isUseTemplateMappers() {
+        return useTemplateMappers;
+    }
+
+    public void setUseTemplateMappers(Boolean useTemplateMappers) {
+        this.useTemplateMappers = useTemplateMappers;
+    }
+
 }
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 f0bf09e..dc575c4 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
@@ -16,6 +16,7 @@ public class ClientTemplateRepresentation {
     protected String name;
     protected String description;
     protected String protocol;
+    protected Boolean fullScopeAllowed;
     protected List<ProtocolMapperRepresentation> protocolMappers;
 
     public String getId() {
@@ -58,4 +59,12 @@ public class ClientTemplateRepresentation {
     public void setProtocol(String protocol) {
         this.protocol = protocol;
     }
+
+    public Boolean isFullScopeAllowed() {
+        return fullScopeAllowed;
+    }
+
+    public void setFullScopeAllowed(Boolean fullScopeAllowed) {
+        this.fullScopeAllowed = fullScopeAllowed;
+    }
 }
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index f7a3c25..cebb3d4 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1088,6 +1088,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 client : function(ClientLoader) {
                     return ClientLoader();
                 },
+                templates : function(ClientTemplateListLoader) {
+                    return ClientTemplateListLoader();
+                },
                 clients : function(ClientListLoader) {
                     return ClientListLoader();
                 }
@@ -1202,6 +1205,21 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'ClientTemplateDetailCtrl'
         })
+        .when('/realms/:realm/client-templates/:template/scope-mappings', {
+            templateUrl : resourceUrl + '/partials/client-template-scope-mappings.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                template : function(ClientTemplateLoader) {
+                    return ClientTemplateLoader();
+                },
+                clients : function(ClientListLoader) {
+                    return ClientListLoader();
+                }
+            },
+            controller : 'ClientTemplateScopeMappingCtrl'
+        })
         .when('/realms/:realm/clients', {
             templateUrl : resourceUrl + '/partials/client-list.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index a6a9130..ec28ef4 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -1089,8 +1089,8 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
     };
 });
 
-module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, Notifications,
-                                                          Client,
+module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, templates, Notifications,
+                                                          Client, ClientTemplate,
                                                           ClientRealmScopeMapping, ClientClientScopeMapping, ClientRole,
                                                           ClientAvailableRealmScopeMapping, ClientAvailableClientScopeMapping,
                                                           ClientCompositeRealmScopeMapping, ClientCompositeClientScopeMapping) {
@@ -1107,8 +1107,20 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien
     $scope.clientMappings = [];
     $scope.dummymodel = [];
 
+    if (client.clientTemplate) {
+        for (var i = 0; i < templates.length; i++) {
+            if (templates[i].name == client.clientTemplate) {
+                ClientTemplate.get({realm: realm.realm, template: templates[i].id}, function(data) {
+                    $scope.template = data;
+                });
+                break;
+            }
+        }
 
-    $scope.changeFullScopeAllowed = function() {
+    }
+
+
+    $scope.changeFlag = function() {
         Client.update({
             realm : realm.realm,
             client : client.id
@@ -1122,6 +1134,7 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien
 
 
 
+
     function updateRealmRoles() {
         $scope.realmRoles = ClientAvailableRealmScopeMapping.query({realm : realm.realm, client : client.id});
         $scope.realmMappings = ClientRealmScopeMapping.query({realm : realm.realm, client : client.id});
@@ -1420,6 +1433,7 @@ module.controller('AddBuiltinProtocolMapperCtrl', function($scope, realm, client
 });
 
 module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client, templates, serverInfo,
+                                                           Client,
                                                            ClientProtocolMappersByProtocol, ClientProtocolMapper,
                                                            $route, Dialog, Notifications) {
     $scope.realm = realm;
@@ -1435,6 +1449,16 @@ module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client
             }
         }
     }
+    $scope.changeFlag = function() {
+        Client.update({
+            realm : realm.realm,
+            client : client.id
+        }, $scope.client, function() {
+            $scope.changed = false;
+            client = angular.copy($scope.client);
+            Notifications.success("Client updated.");
+        });
+    }
 
     var protocolMappers = serverInfo.protocolMapperTypes[client.protocol];
     var mapperTypes = {};
@@ -1910,6 +1934,104 @@ module.controller('ClientTemplateAddBuiltinProtocolMapperCtrl', function($scope,
 });
 
 
+module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, realm, template, clients, Notifications,
+                                                     ClientTemplate,
+                                                     ClientTemplateRealmScopeMapping, ClientTemplateClientScopeMapping, ClientRole,
+                                                     ClientTemplateAvailableRealmScopeMapping, ClientTemplateAvailableClientScopeMapping,
+                                                     ClientTemplateCompositeRealmScopeMapping, ClientTemplateCompositeClientScopeMapping) {
+    $scope.realm = realm;
+    $scope.template = angular.copy(template);
+    $scope.selectedRealmRoles = [];
+    $scope.selectedRealmMappings = [];
+    $scope.realmMappings = [];
+    $scope.clients = clients;
+    $scope.clientRoles = [];
+    $scope.clientComposite = [];
+    $scope.selectedClientRoles = [];
+    $scope.selectedClientMappings = [];
+    $scope.clientMappings = [];
+    $scope.dummymodel = [];
+
+
+    $scope.changeFullScopeAllowed = function() {
+        ClientTemplate.update({
+            realm : realm.realm,
+            template : template.id
+        }, $scope.template, function() {
+            $scope.changed = false;
+            template = angular.copy($scope.template);
+            updateTemplateRealmRoles();
+            Notifications.success("Scope mappings updated.");
+        });
+    }
+
+
+
+    function updateTemplateRealmRoles() {
+        $scope.realmRoles = ClientTemplateAvailableRealmScopeMapping.query({realm : realm.realm, template : template.id});
+        $scope.realmMappings = ClientTemplateRealmScopeMapping.query({realm : realm.realm, template : template.id});
+        $scope.realmComposite = ClientTemplateCompositeRealmScopeMapping.query({realm : realm.realm, template : template.id});
+    }
+
+    function updateTemplateClientRoles() {
+        if ($scope.targetClient) {
+            $scope.clientRoles = ClientTemplateAvailableClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
+            $scope.clientMappings = ClientTemplateClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
+            $scope.clientComposite = ClientTemplateCompositeClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
+        } else {
+            $scope.clientRoles = null;
+            $scope.clientMappings = null;
+            $scope.clientComposite = null;
+        }
+    }
+
+    $scope.changeClient = function() {
+        updateTemplateClientRoles();
+    };
+
+    $scope.addRealmRole = function() {
+        var roles = $scope.selectedRealmRoles;
+        $scope.selectedRealmRoles = [];
+        $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/scope-mappings/realm',
+            roles).success(function() {
+                updateTemplateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+            });
+    };
+
+    $scope.deleteRealmRole = function() {
+        var roles = $scope.selectedRealmMappings;
+        $scope.selectedRealmMappings = [];
+        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id +  '/scope-mappings/realm',
+            {data : roles, headers : {"content-type" : "application/json"}}).success(function () {
+                updateTemplateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+            });
+    };
+
+    $scope.addClientRole = function() {
+        var roles = $scope.selectedClientRoles;
+        $scope.selectedClientRoles = [];
+        $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id +  '/scope-mappings/clients/' + $scope.targetClient.id,
+            roles).success(function () {
+                updateTemplateClientRoles();
+                Notifications.success("Scope mappings updated.");
+            });
+    };
+
+    $scope.deleteClientRole = function() {
+        var roles = $scope.selectedClientMappings;
+        $scope.selectedClientMappings = [];
+        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id +  '/scope-mappings/clients/' + $scope.targetClient.id,
+            {data : roles, headers : {"content-type" : "application/json"}}).success(function () {
+                updateTemplateClientRoles();
+                Notifications.success("Scope mappings updated.");
+            });
+    };
+
+    updateTemplateRealmRoles();
+});
+
 
 
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 7a47a83..b0d9567 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -848,6 +848,52 @@ module.factory('ClientTemplateProtocolMappersByProtocol', function($resource) {
     });
 });
 
+module.factory('ClientTemplateRealmScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm', {
+        realm : '@realm',
+        template : '@template'
+    });
+});
+
+module.factory('ClientTemplateAvailableRealmScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm/available', {
+        realm : '@realm',
+        template : '@template'
+    });
+});
+
+module.factory('ClientTemplateCompositeRealmScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm/composite', {
+        realm : '@realm',
+        template : '@template'
+    });
+});
+
+module.factory('ClientTemplateClientScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient', {
+        realm : '@realm',
+        template : '@template',
+        targetClient : '@targetClient'
+    });
+});
+
+module.factory('ClientTemplateAvailableClientScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient/available', {
+        realm : '@realm',
+        template : '@template',
+        targetClient : '@targetClient'
+    });
+});
+
+module.factory('ClientTemplateCompositeClientScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient/composite', {
+        realm : '@realm',
+        template : '@template',
+        targetClient : '@targetClient'
+    });
+});
+
+
 module.factory('ClientSessionStats', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/clients/:client/session-stats', {
         realm : '@realm',
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
index 9b98065..86b52e1 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
@@ -7,6 +7,20 @@
 
     <kc-tabs-client></kc-tabs-client>
 
+    <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+        <fieldset class="border-top">
+            <div class="form-group" ng-show="client.clientTemplate">
+                <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Mappers</label>
+                <kc-tooltip>Inherit mappers from client template</kc-tooltip>
+                <div class="col-md-1">
+                    <input ng-model="client.useTemplateMappers" ng-click="changeFlag()" name="useTemplateScope" id="useTemplateScope" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                </div>
+                <div class="col-md-2">
+                    <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/mappers">view template mappers</a>
+                </div>
+            </div>
+        </fieldset>
+    </form>
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
@@ -24,7 +38,6 @@
                     <div class="pull-right" data-ng-show="access.manageClients">
                         <a class="btn btn-default" href="#/create/client/{{realm.realm}}/{{client.id}}/mappers">{{:: 'create' | translate}}</a>
                         <a class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/add-mappers">{{:: 'add-builtin' | translate}}</a>
-                        <a ng-show="template" class="btn btn-default" href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/mappers">Inherited Template Mappers</a>
                     </div>
                 </div>
             </th>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
index cd63f49..8075065 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
@@ -11,17 +11,37 @@
         <p class="subtitle"></p>
         <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
             <fieldset class="border-top">
-                <div class="form-group">
+                <div class="form-group" ng-show="client.clientTemplate">
+                    <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Scope</label>
+                    <kc-tooltip>Inherit scope from client template</kc-tooltip>
+                    <div class="col-md-1">
+                        <input ng-model="client.useTemplateScope" ng-click="changeFlag()" name="useTemplateScope" id="useTemplateScope" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                    </div>
+                    <div class="col-md-2">
+                        <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/scope-mappings">view template scope</a>
+                    </div>
+                </div>
+                <div class="form-group" ng-hide="client.useTemplateScope && template && template.fullScopeAllowed">
                     <label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
                     <kc-tooltip>{{:: 'full-scope-allowed.tooltip' | translate}}</kc-tooltip>
                     <div class="col-md-6">
-                        <input ng-model="client.fullScopeAllowed" ng-click="changeFullScopeAllowed()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                        <input ng-model="client.fullScopeAllowed" ng-click="changeFlag()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                    </div>
+                </div>
+                <div class="form-group" ng-show="client.useTemplateScope && template && template.fullScopeAllowed">
+                    <label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
+                    <kc-tooltip>Client template has full scope allowed, which means this client will have the full scope of all roles.</kc-tooltip>
+                    <div class="col-md-1">
+                        <input ng-model="template.fullScopeAllowed" name="fullScopeAllowed" id="fullScopeAllowed" ng-disabled="true" onoffswitch />
+                    </div>
+                    <div class="col-md-1">
+                        <i>inherited</i>
                     </div>
                 </div>
             </fieldset>
         </form>
 
-        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.fullScopeAllowed">
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.fullScopeAllowed" data-ng-hide="client.useTemplateScope && template && template.fullScopeAllowed">
             <div class="form-group">
                 <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
                 <div class="col-md-10">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-template-scope-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-template-scope-mappings.html
new file mode 100755
index 0000000..6903071
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-template-scope-mappings.html
@@ -0,0 +1,117 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/client-templates">Client Templates</a></li>
+        <li>{{template.name}}</li>
+    </ol>
+
+    <kc-tabs-client-template></kc-tabs-client-template>
+
+        <h2><span>{{template.name}}</span> {{:: 'scope-mappings' | translate}} </h2>
+        <p class="subtitle"></p>
+        <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+            <fieldset class="border-top">
+                <div class="form-group">
+                    <label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
+                    <kc-tooltip>{{:: 'full-scope-allowed.tooltip' | translate}}</kc-tooltip>
+                    <div class="col-md-6">
+                        <input ng-model="template.fullScopeAllowed" ng-click="changeFullScopeAllowed()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!template.fullScopeAllowed">
+            <div class="form-group">
+                <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
+                <div class="col-md-10">
+                    <div class="row">
+                        <div class="col-md-3">
+                            <label class="control-label" for="available">{{:: 'available-roles' | translate}}</label>
+                            <kc-tooltip>{{:: 'scope.available-roles.tooltip' | translate}}</kc-tooltip>
+
+                            <select id="available" class="form-control" multiple size="5"
+                                    ng-multiple="true"
+                                    ng-model="selectedRealmRoles"
+                                    ng-options="r.name for r in realmRoles">
+                            </select>
+                            <button ng-disabled="selectedRealmRoles.length == 0" class="btn btn-default" type="submit" ng-click="addRealmRole()">
+                                {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+                            </button>
+                        </div>
+                        <div class="col-md-3">
+                            <label class="control-label" for="assigned">{{:: 'assigned-roles' | translate}}</label>
+                            <kc-tooltip>{{:: 'assigned-roles.tooltip' | translate}}</kc-tooltip>
+                            <select id="assigned" class="form-control" multiple size=5
+                                    ng-multiple="true"
+                                    ng-model="selectedRealmMappings"
+                                    ng-options="r.name for r in realmMappings">
+                            </select>
+                            <button ng-disabled="selectedRealmMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmRole()">
+                                <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+                            </button>
+                        </div>
+                        <div class="col-md-3">
+                            <label class="control-label" for="realm-composite">{{:: 'effective-roles' | translate}}  </label>
+                            <kc-tooltip>{{:: 'realm.effective-roles.tooltip' | translate}}</kc-tooltip>
+                            <select id="realm-composite" class="form-control" multiple size=5
+                                    disabled="true"
+                                    ng-model="dummymodel"
+                                    ng-options="r.name for r in realmComposite">
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            <div class="form-group">
+                <label class="col-md-2 control-label" class="control-label">
+                    <span>{{:: 'client-roles' | translate}}</span>
+                    <select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients" ng-disabled="false"></select>
+                </label>
+
+                <div class="col-md-10">
+                    <div class="row" data-ng-hide="targetClient">
+                        <div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
+                    </div>
+                    <div class="row" data-ng-show="targetClient">
+                        <div class="col-md-3">
+                            <label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
+                            <kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>
+                            <select id="available-client" class="form-control" multiple size="5"
+                                    ng-multiple="true"
+                                    ng-model="selectedClientRoles"
+                                    ng-options="r.name for r in clientRoles">
+                            </select>
+                            <button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientRole()">
+                                {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+                            </button>
+                        </div>
+                        <div class="col-md-3">
+                            <label class="control-label" for="assigned-client">{{:: 'assigned-roles' | translate}}</label>
+                            <kc-tooltip>{{:: 'client.assigned-roles.tooltip' | translate}}</kc-tooltip>
+                            <select id="assigned-client" class="form-control" multiple size=5
+                                    ng-multiple="true"
+                                    ng-model="selectedClientMappings"
+                                    ng-options="r.name for r in clientMappings">
+                            </select>
+                            <button ng-disabled="selectedClientMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteClientRole()">
+                                <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+                            </button>
+                        </div>
+                        <div class="col-md-3">
+                            <label class="control-label" for="client-composite">{{:: 'effective-roles' | translate}}</label>
+                            <kc-tooltip>{{:: 'client.effective-roles.tooltip' | translate}}</kc-tooltip>
+                            <select id="client-composite" class="form-control" multiple size=5
+                                    disabled="true"
+                                    ng-model="dummymodel"
+                                    ng-options="r.name for r in clientComposite">
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </form>
+    </div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-template.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-template.html
index 728e091..0bbdaf0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-template.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-template.html
@@ -12,5 +12,9 @@
             <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/mappers">{{:: 'mappers' | translate}}</a>
             <kc-tooltip>{{:: 'mappers.tooltip' | translate}}</kc-tooltip>
         </li>
+        <li ng-class="{active: path[4] == 'scope-mappings'}" >
+            <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/scope-mappings">{{:: 'scope' | translate}}</a>
+            <kc-tooltip>{{:: 'scope.tooltip' | translate}}</kc-tooltip>
+        </li>
     </ul>
 </div>
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientTemplateResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientTemplateResource.java
index 0a5d8ff..a5665cd 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientTemplateResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientTemplateResource.java
@@ -27,6 +27,9 @@ public interface ClientTemplateResource {
     @Path("protocol-mappers")
     public ProtocolMappersResource getProtocolMappers();
 
+    @Path("/scope-mappings")
+    public RoleMappingResource getScopeMappings();
+
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public ClientTemplateRepresentation toRepresentation();
@@ -37,4 +40,6 @@ public interface ClientTemplateResource {
 
     @DELETE
     public void remove();
+
+
 }
\ No newline at end of file
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 f7e0305..475edf2 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -8,7 +8,7 @@ import java.util.Set;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public interface ClientModel extends RoleContainerModel,  ProtocolMapperContainerModel {
+public interface ClientModel extends RoleContainerModel,  ProtocolMapperContainerModel, ScopeContainerModel {
 
     // COMMON ATTRIBUTES
 
@@ -74,7 +74,6 @@ public interface ClientModel extends RoleContainerModel,  ProtocolMapperContaine
 
     void updateDefaultRoles(String[] defaultRoles);
 
-    Set<RoleModel> getClientScopeMappings(ClientModel client);
 
     boolean isBearerOnly();
     void setBearerOnly(boolean only);
@@ -93,9 +92,6 @@ public interface ClientModel extends RoleContainerModel,  ProtocolMapperContaine
     String getRegistrationToken();
     void setRegistrationToken(String registrationToken);
 
-    boolean isFullScopeAllowed();
-    void setFullScopeAllowed(boolean value);
-
     String getProtocol();
     void setProtocol(String protocol);
 
@@ -126,16 +122,16 @@ public interface ClientModel extends RoleContainerModel,  ProtocolMapperContaine
     boolean isServiceAccountsEnabled();
     void setServiceAccountsEnabled(boolean serviceAccountsEnabled);
 
-    Set<RoleModel> getScopeMappings();
-    void addScopeMapping(RoleModel role);
-    void deleteScopeMapping(RoleModel role);
-    Set<RoleModel> getRealmScopeMappings();
-    boolean hasScope(RoleModel role);
-
     RealmModel getRealm();
 
     ClientTemplateModel getClientTemplate();
     void setClientTemplate(ClientTemplateModel template);
+    boolean useTemplateScope();
+    void setUseTemplateScope(boolean flag);
+    boolean useTemplateMappers();
+    void setUseTemplateMappers(boolean flag);
+    boolean useTemplateConfig();
+    void setUseTemplateConfig(boolean flag);
 
     /**
      * Time in seconds since epoc
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 f7d67ac..f3c0f59 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java
@@ -8,7 +8,7 @@ import java.util.Set;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public interface ClientTemplateModel extends ProtocolMapperContainerModel {
+public interface ClientTemplateModel extends ProtocolMapperContainerModel, ScopeContainerModel {
     String getId();
 
     String getName();
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
index 24e5fc6..04daf9f 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
@@ -49,6 +49,9 @@ public class ClientEntity extends AbstractIdentifiableEntity {
     private List<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
     private List<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
     private String clientTemplate;
+    private boolean useTemplateConfig;
+    private boolean useTemplateScope;
+    private boolean useTemplateMappers;
 
     public String getClientId() {
         return clientId;
@@ -309,5 +312,29 @@ public class ClientEntity extends AbstractIdentifiableEntity {
     public void setClientTemplate(String clientTemplate) {
         this.clientTemplate = clientTemplate;
     }
+
+    public boolean isUseTemplateConfig() {
+        return useTemplateConfig;
+    }
+
+    public void setUseTemplateConfig(boolean useTemplateConfig) {
+        this.useTemplateConfig = useTemplateConfig;
+    }
+
+    public boolean isUseTemplateScope() {
+        return useTemplateScope;
+    }
+
+    public void setUseTemplateScope(boolean useTemplateScope) {
+        this.useTemplateScope = useTemplateScope;
+    }
+
+    public boolean isUseTemplateMappers() {
+        return useTemplateMappers;
+    }
+
+    public void setUseTemplateMappers(boolean useTemplateMappers) {
+        this.useTemplateMappers = useTemplateMappers;
+    }
 }
 
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 849eaa1..8ca932f 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
@@ -14,6 +14,8 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity {
     private String description;
     private String realmId;
     private String protocol;
+    private boolean fullScopeAllowed;
+    private List<String> scopeIds = new ArrayList<String>();
     private List<ProtocolMapperEntity> protocolMappers = new ArrayList<ProtocolMapperEntity>();
 
     public String getName() {
@@ -55,5 +57,21 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity {
     public void setProtocol(String protocol) {
         this.protocol = protocol;
     }
+
+    public boolean isFullScopeAllowed() {
+        return fullScopeAllowed;
+    }
+
+    public void setFullScopeAllowed(boolean fullScopeAllowed) {
+        this.fullScopeAllowed = fullScopeAllowed;
+    }
+
+    public List<String> getScopeIds() {
+        return scopeIds;
+    }
+
+    public void setScopeIds(List<String> scopeIds) {
+        this.scopeIds = scopeIds;
+    }
 }
 
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 8c6af88..f08eee4 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -15,7 +15,9 @@ import org.keycloak.models.KeycloakSessionTask;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.ScopeContainerModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProviderModel;
@@ -38,6 +40,7 @@ import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -521,4 +524,20 @@ public final class KeycloakModelUtils {
         }
         return found;
     }
+
+    public static Set<RoleModel> getClientScopeMappings(ClientModel client, ScopeContainerModel container) {
+        Set<RoleModel> mappings = container.getScopeMappings();
+        Set<RoleModel> result = new HashSet<>();
+        for (RoleModel role : mappings) {
+            RoleContainerModel roleContainer = role.getContainer();
+            if (roleContainer instanceof ClientModel) {
+                if (client.getId().equals(((ClientModel)roleContainer).getId())) {
+                    result.add(role);
+                }
+
+            }
+        }
+        return result;
+    }
+
 }
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 7b6a198..e891582 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
@@ -419,6 +419,7 @@ public class ModelToRepresentation {
             }
             rep.setProtocolMappers(mappings);
         }
+        rep.setFullScopeAllowed(clientModel.isFullScopeAllowed());
 
         return rep;
     }
@@ -476,6 +477,9 @@ public class ModelToRepresentation {
             }
             rep.setProtocolMappers(mappings);
         }
+        rep.setUseTemplateMappers(clientModel.useTemplateMappers());
+        rep.setUseTemplateConfig(clientModel.useTemplateConfig());
+        rep.setUseTemplateScope(clientModel.useTemplateScope());
 
         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 4c7dad1..2d99c3b 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
@@ -802,11 +802,6 @@ public class RepresentationToModel {
         if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
         if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
         if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
-        if (resourceRep.isFullScopeAllowed() != null) {
-            client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
-        } else {
-            client.setFullScopeAllowed(!client.isConsentRequired());
-        }
         if (resourceRep.getNodeReRegistrationTimeout() != null) {
             client.setNodeReRegistrationTimeout(resourceRep.getNodeReRegistrationTimeout());
         } else {
@@ -893,6 +888,26 @@ public class RepresentationToModel {
             }
         }
 
+        if (resourceRep.isFullScopeAllowed() != null) {
+            client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
+        } else {
+            if (client.getClientTemplate() != null) {
+                client.setFullScopeAllowed(!client.isConsentRequired() && client.getClientTemplate().isFullScopeAllowed());
+
+            } else {
+                client.setFullScopeAllowed(!client.isConsentRequired());
+            }
+        }
+        if (resourceRep.isUseTemplateConfig() != null) client.setUseTemplateConfig(resourceRep.isUseTemplateConfig());
+        else client.setUseTemplateConfig(resourceRep.getClientTemplate() != null);
+
+        if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope());
+        else client.setUseTemplateScope(resourceRep.getClientTemplate() != null);
+
+        if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
+        else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
+
+
         return client;
     }
 
@@ -949,14 +964,23 @@ public class RepresentationToModel {
             }
         }
 
+        if (rep.isUseTemplateConfig() != null) resource.setUseTemplateConfig(rep.isUseTemplateConfig());
+        if (rep.isUseTemplateScope() != null) resource.setUseTemplateScope(rep.isUseTemplateScope());
+        if (rep.isUseTemplateMappers() != null) resource.setUseTemplateMappers(rep.isUseTemplateMappers());
+
+
         if (rep.getClientTemplate() != null) {
             if (rep.getClientTemplate().equals(ClientTemplateRepresentation.NONE)) {
                 resource.setClientTemplate(null);
             } else {
                 RealmModel realm = resource.getRealm();
                 for (ClientTemplateModel template : realm.getClientTemplates()) {
+
                     if (template.getName().equals(rep.getClientTemplate())) {
                         resource.setClientTemplate(template);
+                        if (rep.isUseTemplateConfig() == null) resource.setUseTemplateConfig(true);
+                        if (rep.isUseTemplateScope() == null) resource.setUseTemplateScope(true);
+                        if (rep.isUseTemplateMappers() == null) resource.setUseTemplateMappers(true);
                         break;
                     }
                 }
@@ -984,7 +1008,7 @@ public class RepresentationToModel {
         if (resourceRep.getName() != null) client.setName(resourceRep.getName());
         if(resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription());
         if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
-
+        if (resourceRep.isFullScopeAllowed() != null) client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
         if (resourceRep.getProtocolMappers() != null) {
             // first, remove all default/built in mappers
             Set<ProtocolMapperModel> mappers = client.getProtocolMappers();
@@ -1001,6 +1025,9 @@ public class RepresentationToModel {
     public static void updateClientTemplate(ClientTemplateRepresentation rep, ClientTemplateModel resource) {
         if (rep.getName() != null) resource.setName(rep.getName());
         if (rep.getDescription() != null) resource.setDescription(rep.getDescription());
+        if (rep.isFullScopeAllowed() != null) {
+            resource.setFullScopeAllowed(rep.isFullScopeAllowed());
+        }
 
 
         if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index d96e6bc..164fc74 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -69,6 +69,47 @@ public class ClientAdapter implements ClientModel {
 
     }
 
+    @Override
+    public boolean useTemplateScope() {
+        if (updated != null) return updated.useTemplateScope();
+        return cached.isUseTemplateScope();
+    }
+
+    @Override
+    public void setUseTemplateScope(boolean value) {
+        getDelegateForUpdate();
+        updated.setUseTemplateScope(value);
+
+    }
+
+    @Override
+    public boolean useTemplateConfig() {
+        if (updated != null) return updated.useTemplateConfig();
+        return cached.isUseTemplateConfig();
+    }
+
+    @Override
+    public void setUseTemplateConfig(boolean value) {
+        getDelegateForUpdate();
+        updated.setUseTemplateConfig(value);
+
+    }
+
+    @Override
+    public boolean useTemplateMappers() {
+        if (updated != null) return updated.useTemplateMappers();
+        return cached.isUseTemplateMappers();
+    }
+
+    @Override
+    public void setUseTemplateMappers(boolean value) {
+        getDelegateForUpdate();
+        updated.setUseTemplateMappers(value);
+
+    }
+
+
+
     public void addWebOrigin(String webOrigin) {
         getDelegateForUpdate();
         updated.addWebOrigin(webOrigin);
@@ -413,25 +454,6 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
-    public Set<RoleModel> getClientScopeMappings(ClientModel client) {
-        Set<RoleModel> roleMappings = client.getScopeMappings();
-
-        Set<RoleModel> appRoles = new HashSet<RoleModel>();
-        for (RoleModel role : roleMappings) {
-            RoleContainerModel container = role.getContainer();
-            if (container instanceof RealmModel) {
-            } else {
-                ClientModel app = (ClientModel)container;
-                if (app.getId().equals(getId())) {
-                    appRoles.add(role);
-                }
-            }
-        }
-
-        return appRoles;
-    }
-
-    @Override
     public boolean isBearerOnly() {
         if (updated != null) return updated.isBearerOnly();
         return cached.isBearerOnly();
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 2a1674d..13b68aa 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
@@ -133,6 +133,70 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
     }
 
     @Override
+    public boolean isFullScopeAllowed() {
+        if (updated != null) return updated.isFullScopeAllowed();
+        return cached.isFullScopeAllowed();
+    }
+
+    @Override
+    public void setFullScopeAllowed(boolean value) {
+        getDelegateForUpdate();
+        updated.setFullScopeAllowed(value);
+
+    }
+
+    public Set<RoleModel> getScopeMappings() {
+        if (updated != null) return updated.getScopeMappings();
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        for (String id : cached.getScope()) {
+            roles.add(cacheSession.getRoleById(id, getRealm()));
+
+        }
+        return roles;
+    }
+
+    public void addScopeMapping(RoleModel role) {
+        getDelegateForUpdate();
+        updated.addScopeMapping(role);
+    }
+
+    public void deleteScopeMapping(RoleModel role) {
+        getDelegateForUpdate();
+        updated.deleteScopeMapping(role);
+    }
+
+    public Set<RoleModel> getRealmScopeMappings() {
+        Set<RoleModel> roleMappings = getScopeMappings();
+
+        Set<RoleModel> appRoles = new HashSet<RoleModel>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof RealmModel) {
+                if (((RealmModel) container).getId().equals(cachedRealm.getId())) {
+                    appRoles.add(role);
+                }
+            }
+        }
+
+        return appRoles;
+    }
+
+    @Override
+    public boolean hasScope(RoleModel role) {
+        if (updated != null) return updated.hasScope(role);
+        if (cached.isFullScopeAllowed() || cached.getScope().contains(role.getId())) return true;
+
+        Set<RoleModel> roles = getScopeMappings();
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
+        }
+       return false;
+    }
+
+
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || !(o instanceof ClientModel)) return false;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index dbf4754..ab83077 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -56,6 +56,9 @@ public class CachedClient implements Serializable {
     private int nodeReRegistrationTimeout;
     private Map<String, Integer> registeredNodes;
     private String clientTemplate;
+    private boolean useTemplateScope;
+    private boolean useTemplateConfig;
+    private boolean useTemplateMappers;
 
     public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
         id = model.getId();
@@ -102,6 +105,9 @@ public class CachedClient implements Serializable {
         if (model.getClientTemplate() != null) {
             clientTemplate = model.getClientTemplate().getId();
         }
+        useTemplateConfig = model.useTemplateConfig();
+        useTemplateMappers = model.useTemplateMappers();
+        useTemplateScope = model.useTemplateScope();
     }
     public String getId() {
         return id;
@@ -238,4 +244,16 @@ public class CachedClient implements Serializable {
     public String getClientTemplate() {
         return clientTemplate;
     }
+
+    public boolean isUseTemplateScope() {
+        return useTemplateScope;
+    }
+
+    public boolean isUseTemplateConfig() {
+        return useTemplateConfig;
+    }
+
+    public boolean isUseTemplateMappers() {
+        return useTemplateMappers;
+    }
 }
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 2df28c1..58bcc12 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
@@ -28,6 +28,8 @@ public class CachedClientTemplate implements Serializable {
     private String description;
     private String realm;
     private String protocol;
+    private boolean fullScopeAllowed;
+    private Set<String> scope = new HashSet<String>();
     private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
 
     public CachedClientTemplate(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) {
@@ -36,9 +38,13 @@ public class CachedClientTemplate implements Serializable {
         description = model.getDescription();
         this.realm = realm.getId();
         protocol = model.getProtocol();
+        fullScopeAllowed = model.isFullScopeAllowed();
         for (ProtocolMapperModel mapper : model.getProtocolMappers()) {
             this.protocolMappers.add(mapper);
         }
+        for (RoleModel role : model.getScopeMappings())  {
+            scope.add(role.getId());
+        }
     }
     public String getId() {
         return id;
@@ -63,4 +69,12 @@ public class CachedClientTemplate implements Serializable {
     public String getProtocol() {
         return protocol;
     }
+
+    public boolean isFullScopeAllowed() {
+        return fullScopeAllowed;
+    }
+
+    public Set<String> getScope() {
+        return scope;
+    }
 }
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 a8abd11..0d9eea4 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
@@ -206,7 +206,7 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
-    public Set<RoleModel> getRealmScopeMappings() {
+         public Set<RoleModel> getRealmScopeMappings() {
         Set<RoleModel> roleMappings = getScopeMappings();
 
         Set<RoleModel> appRoles = new HashSet<>();
@@ -238,7 +238,8 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public void addScopeMapping(RoleModel role) {
-        if (hasScope(role)) return;
+        Set<RoleModel> roles = getScopeMappings();
+        if (roles.contains(role)) return;
         ScopeMappingEntity entity = new ScopeMappingEntity();
         entity.setClient(getEntity());
         RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
@@ -319,6 +320,39 @@ public class ClientAdapter implements ClientModel {
 
     }
 
+    @Override
+    public boolean useTemplateScope() {
+        return entity.isUseTemplateScope();
+    }
+
+    @Override
+    public void setUseTemplateScope(boolean flag) {
+        entity.setUseTemplateScope(flag);
+
+    }
+
+    @Override
+    public boolean useTemplateMappers() {
+        return entity.isUseTemplateMappers();
+    }
+
+    @Override
+    public void setUseTemplateMappers(boolean flag) {
+        entity.setUseTemplateMappers(flag);
+
+    }
+
+    @Override
+    public boolean useTemplateConfig() {
+        return entity.isUseTemplateConfig();
+    }
+
+    @Override
+    public void setUseTemplateConfig(boolean flag) {
+        entity.setUseTemplateConfig(flag);
+
+    }
+
     public static boolean contains(String str, String[] array) {
         for (String s : array) {
             if (str.equals(s)) return true;
@@ -604,6 +638,7 @@ public class ClientAdapter implements ClientModel {
         String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
         em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
         em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", role).executeUpdate();
+        em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", role).executeUpdate();
         role.setClient(null);
         em.flush();
         em.remove(role);
@@ -642,28 +677,6 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
-    public Set<RoleModel> getClientScopeMappings(ClientModel client) {
-        Set<RoleModel> roleMappings = client.getScopeMappings();
-
-        Set<RoleModel> appRoles = new HashSet<RoleModel>();
-        for (RoleModel role : roleMappings) {
-            RoleContainerModel container = role.getContainer();
-            if (container instanceof RealmModel) {
-            } else {
-                ClientModel app = (ClientModel)container;
-                if (app.getId().equals(getId())) {
-                    appRoles.add(role);
-                }
-            }
-        }
-
-        return appRoles;
-    }
-
-
-
-
-    @Override
     public List<String> getDefaultRoles() {
         Collection<RoleEntity> entities = entity.getDefaultRoles();
         List<String> roles = new ArrayList<String>();
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 8e92e79..2d5c78b 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
@@ -14,6 +14,7 @@ import org.keycloak.models.jpa.entities.ClientTemplateEntity;
 import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
 import org.keycloak.models.jpa.entities.RoleEntity;
 import org.keycloak.models.jpa.entities.ScopeMappingEntity;
+import org.keycloak.models.jpa.entities.TemplateScopeMappingEntity;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
@@ -203,6 +204,88 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
         return mapping;
     }
 
+    @Override
+    public boolean isFullScopeAllowed() {
+        return entity.isFullScopeAllowed();
+    }
+
+    @Override
+    public void setFullScopeAllowed(boolean value) {
+        entity.setFullScopeAllowed(value);
+    }
+
+    @Override
+    public Set<RoleModel> getRealmScopeMappings() {
+        Set<RoleModel> roleMappings = getScopeMappings();
+
+        Set<RoleModel> appRoles = new HashSet<>();
+        for (RoleModel role : roleMappings) {
+            RoleContainerModel container = role.getContainer();
+            if (container instanceof RealmModel) {
+                if (((RealmModel) container).getId().equals(realm.getId())) {
+                    appRoles.add(role);
+                }
+            }
+        }
+
+        return appRoles;
+    }
+
+    @Override
+    public Set<RoleModel> getScopeMappings() {
+        TypedQuery<String> query = em.createNamedQuery("clientTemplateScopeMappingIds", String.class);
+        query.setParameter("template", getEntity());
+        List<String> ids = query.getResultList();
+        Set<RoleModel> roles = new HashSet<RoleModel>();
+        for (String roleId : ids) {
+            RoleModel role = realm.getRoleById(roleId);
+            if (role == null) continue;
+            roles.add(role);
+        }
+        return roles;
+    }
+
+    @Override
+    public void addScopeMapping(RoleModel role) {
+        if (hasScope(role)) return;
+        TemplateScopeMappingEntity entity = new TemplateScopeMappingEntity();
+        entity.setTemplate(getEntity());
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        entity.setRole(roleEntity);
+        em.persist(entity);
+        em.flush();
+        em.detach(entity);
+    }
+
+    @Override
+    public void deleteScopeMapping(RoleModel role) {
+        TypedQuery<TemplateScopeMappingEntity> query = getRealmScopeMappingQuery(role);
+        List<TemplateScopeMappingEntity> results = query.getResultList();
+        if (results.size() == 0) return;
+        for (TemplateScopeMappingEntity entity : results) {
+            em.remove(entity);
+        }
+    }
+
+    protected TypedQuery<TemplateScopeMappingEntity> getRealmScopeMappingQuery(RoleModel role) {
+        TypedQuery<TemplateScopeMappingEntity> query = em.createNamedQuery("templateHasScope", TemplateScopeMappingEntity.class);
+        query.setParameter("template", getEntity());
+        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+        query.setParameter("role", roleEntity);
+        return query;
+    }
+
+    @Override
+    public boolean hasScope(RoleModel role) {
+        if (isFullScopeAllowed()) return true;
+        Set<RoleModel> roles = getScopeMappings();
+        if (roles.contains(role)) return true;
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
+        }
+        return false;
+    }
 
     @Override
     public boolean equals(Object o) {
@@ -219,4 +302,6 @@ public class ClientTemplateAdapter implements ClientTemplateModel {
     }
 
 
+
+
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index 6767e7b..80781b8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -61,6 +61,15 @@ public class ClientEntity {
     @JoinColumn(name = "CLIENT_TEMPLATE_ID")
     protected ClientTemplateEntity clientTemplate;
 
+    @Column(name="USE_TEMPLATE_CONFIG")
+    private boolean useTemplateConfig;
+
+    @Column(name="USE_TEMPLATE_SCOPE")
+    private boolean useTemplateScope;
+
+    @Column(name="USE_TEMPLATE_MAPPERS")
+    private boolean useTemplateMappers;
+
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "REALM_ID")
     protected RealmEntity realm;
@@ -404,4 +413,28 @@ public class ClientEntity {
     public void setClientTemplate(ClientTemplateEntity clientTemplate) {
         this.clientTemplate = clientTemplate;
     }
+
+    public boolean isUseTemplateConfig() {
+        return useTemplateConfig;
+    }
+
+    public void setUseTemplateConfig(boolean useTemplateConfig) {
+        this.useTemplateConfig = useTemplateConfig;
+    }
+
+    public boolean isUseTemplateScope() {
+        return useTemplateScope;
+    }
+
+    public void setUseTemplateScope(boolean useTemplateScope) {
+        this.useTemplateScope = useTemplateScope;
+    }
+
+    public boolean isUseTemplateMappers() {
+        return useTemplateMappers;
+    }
+
+    public void setUseTemplateMappers(boolean useTemplateMappers) {
+        this.useTemplateMappers = useTemplateMappers;
+    }
 }
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 ff4bd14..5da01c4 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
@@ -45,6 +45,8 @@ public class ClientTemplateEntity {
     @Column(name="PROTOCOL")
     private String protocol;
 
+    @Column(name="FULL_SCOPE_ALLOWED")
+    private boolean fullScopeAllowed;
 
     public RealmEntity getRealm() {
         return realm;
@@ -93,4 +95,12 @@ public class ClientTemplateEntity {
     public void setProtocol(String protocol) {
         this.protocol = protocol;
     }
+
+    public boolean isFullScopeAllowed() {
+        return fullScopeAllowed;
+    }
+
+    public void setFullScopeAllowed(boolean fullScopeAllowed) {
+        this.fullScopeAllowed = fullScopeAllowed;
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/TemplateScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/TemplateScopeMappingEntity.java
new file mode 100755
index 0000000..375fd05
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/TemplateScopeMappingEntity.java
@@ -0,0 +1,99 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+        @NamedQuery(name="templateHasScope", query="select m from TemplateScopeMappingEntity m where m.template = :template and m.role = :role"),
+        @NamedQuery(name="clientTemplateScopeMappings", query="select m from TemplateScopeMappingEntity m where m.template = :template"),
+        @NamedQuery(name="clientTemplateScopeMappingIds", query="select m.role.id from TemplateScopeMappingEntity m where m.template = :template"),
+        @NamedQuery(name="deleteTemplateScopeMappingByRole", query="delete from TemplateScopeMappingEntity where role = :role"),
+        @NamedQuery(name="deleteTemplateScopeMappingByClient", query="delete from TemplateScopeMappingEntity where template = :template")
+})
+@Table(name="TEMPLATE_SCOPE_MAPPING")
+@Entity
+@IdClass(TemplateScopeMappingEntity.Key.class)
+public class TemplateScopeMappingEntity {
+
+    @Id
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name = "TEMPLATE_ID")
+    protected ClientTemplateEntity template;
+
+    @Id
+    @ManyToOne(fetch= FetchType.LAZY)
+    @JoinColumn(name="ROLE_ID")
+    protected RoleEntity role;
+
+    public ClientTemplateEntity getTemplate() {
+        return template;
+    }
+
+    public void setTemplate(ClientTemplateEntity template) {
+        this.template = template;
+    }
+
+    public RoleEntity getRole() {
+        return role;
+    }
+
+    public void setRole(RoleEntity role) {
+        this.role = role;
+    }
+
+    public static class Key implements Serializable {
+
+        protected ClientTemplateEntity template;
+
+        protected RoleEntity role;
+
+        public Key() {
+        }
+
+        public Key(ClientTemplateEntity template, RoleEntity role) {
+            this.template = template;
+            this.role = role;
+        }
+
+        public ClientTemplateEntity getTemplate() {
+            return template;
+        }
+
+        public RoleEntity getRole() {
+            return role;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Key key = (Key) o;
+
+            if (template != null ? !template.getId().equals(key.template != null ? key.template.getId() : null) : key.template != null) return false;
+            if (role != null ? !role.getId().equals(key.role != null ? key.role.getId() : null) : key.role != null) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = template != null ? template.getId().hashCode() : 0;
+            result = 31 * result + (role != null ? role.getId().hashCode() : 0);
+            return result;
+        }
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 25df437..25e79f8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -110,6 +110,9 @@ public class JpaRealmProvider implements RealmProvider {
         for (ClientEntity a : new LinkedList<>(realm.getClients())) {
             adapter.removeClient(a.getId());
         }
+        for (ClientTemplateEntity a : new LinkedList<>(realm.getClientTemplates())) {
+            adapter.removeClientTemplate(a.getId());
+        }
 
         em.remove(realm);
 
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 4f96829..ccc3339 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
@@ -1051,6 +1051,7 @@ public class RealmAdapter implements RealmModel {
         String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
         em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
         em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
+        em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
         em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
 
         em.remove(roleEntity);
@@ -2146,9 +2147,12 @@ public class RealmAdapter implements RealmModel {
         if (client == null) {
             return false;
         }
+        em.createNamedQuery("deleteTemplateScopeMappingByClient").setParameter("template", clientEntity).executeUpdate();
+        em.flush();
         em.remove(clientEntity);
         em.flush();
 
+
         return true;
     }
 
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 af448c4..02ccea4 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
@@ -10,6 +10,7 @@ import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.ScopeContainerModel;
 import org.keycloak.models.entities.ProtocolMapperEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
 import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
@@ -621,19 +622,6 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
-    public Set<RoleModel> getClientScopeMappings(ClientModel client) {
-        Set<RoleModel> result = new HashSet<RoleModel>();
-        List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
-
-        for (MongoRoleEntity role : roles) {
-            if (getId().equals(role.getClientId())) {
-                result.add(new RoleAdapter(session, getRealm(), role, this, invocationContext));
-            }
-        }
-        return result;
-    }
-
-    @Override
     public List<String> getDefaultRoles() {
         return getMongoEntity().getDefaultRoles();
     }
@@ -726,4 +714,41 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
         updateMongoEntity();
 
     }
+
+    @Override
+    public boolean useTemplateScope() {
+        return getMongoEntity().isUseTemplateScope();
+    }
+
+    @Override
+    public void setUseTemplateScope(boolean flag) {
+        getMongoEntity().setUseTemplateScope(flag);
+        updateMongoEntity();
+
+    }
+
+    @Override
+    public boolean useTemplateMappers() {
+        return getMongoEntity().isUseTemplateMappers();
+    }
+
+    @Override
+    public void setUseTemplateMappers(boolean flag) {
+        getMongoEntity().setUseTemplateMappers(flag);
+        updateMongoEntity();
+
+    }
+
+    @Override
+    public boolean useTemplateConfig() {
+        return getMongoEntity().isUseTemplateConfig();
+    }
+
+    @Override
+    public void setUseTemplateConfig(boolean flag) {
+        getMongoEntity().setUseTemplateConfig(flag);
+        updateMongoEntity();
+
+    }
+
 }
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 ee19dea..91725bf 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
@@ -209,7 +209,70 @@ public class ClientTemplateAdapter extends AbstractMongoAdapter<MongoClientTempl
         return mapping;
     }
 
+    @Override
+    public boolean isFullScopeAllowed() {
+        return getMongoEntity().isFullScopeAllowed();
+    }
+
+    @Override
+    public void setFullScopeAllowed(boolean value) {
+        getMongoEntity().setFullScopeAllowed(value);
+        updateMongoEntity();
+
+    }
+    @Override
+    public Set<RoleModel> getScopeMappings() {
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfTemplate(this, invocationContext);
+
+        for (MongoRoleEntity role : roles) {
+            if (realm.getId().equals(role.getRealmId())) {
+                result.add(new RoleAdapter(session, realm, role, realm, invocationContext));
+            } else {
+                // Likely applicationRole, but we don't have this application yet
+                result.add(new RoleAdapter(session, realm, role, invocationContext));
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Set<RoleModel> getRealmScopeMappings() {
+        Set<RoleModel> allScopes = getScopeMappings();
+
+        // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
+        Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+        for (RoleModel role : allScopes) {
+            MongoRoleEntity roleEntity = ((RoleAdapter) role).getRole();
 
+            if (realm.getId().equals(roleEntity.getRealmId())) {
+                realmRoles.add(role);
+            }
+        }
+        return realmRoles;
+    }
+
+    @Override
+    public void addScopeMapping(RoleModel role) {
+        getMongoStore().pushItemToList(this.getMongoEntity(), "scopeIds", role.getId(), true, invocationContext);
+    }
+
+    @Override
+    public void deleteScopeMapping(RoleModel role) {
+        getMongoStore().pullItemFromList(this.getMongoEntity(), "scopeIds", role.getId(), invocationContext);
+    }
+
+    @Override
+    public boolean hasScope(RoleModel role) {
+        if (isFullScopeAllowed()) return true;
+        Set<RoleModel> roles = getScopeMappings();
+        if (roles.contains(role)) return true;
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
+        }
+        return false;
+    }
 
     @Override
     public boolean equals(Object o) {
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
index eaf2216..907b29d 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
@@ -4,12 +4,15 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.entities.ClientEntity;
+import org.keycloak.models.entities.ClientTemplateEntity;
 import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
+import org.keycloak.models.mongo.keycloak.adapters.ClientTemplateAdapter;
 import org.keycloak.models.mongo.keycloak.adapters.GroupAdapter;
 import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
 import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
@@ -57,4 +60,17 @@ public class MongoModelUtils {
                 .get();
         return invContext.getMongoStore().loadEntities(MongoRoleEntity.class, query, invContext);
     }
+    public static List<MongoRoleEntity> getAllScopesOfTemplate(ClientTemplateModel template, MongoStoreInvocationContext invContext) {
+        ClientTemplateEntity scopedEntity = ((ClientTemplateAdapter)template).getMongoEntity();
+        List<String> scopeIds = scopedEntity.getScopeIds();
+
+        if (scopeIds == null || scopeIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        DBObject query = new QueryBuilder()
+                .and("_id").in(scopeIds)
+                .get();
+        return invContext.getMongoStore().loadEntities(MongoRoleEntity.class, query, invContext);
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index 346832a..d1a83bb 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -267,7 +267,7 @@ public class TokenManager {
 
         Set<String> requestedProtocolMappers = new HashSet<String>();
         ClientTemplateModel clientTemplate = client.getClientTemplate();
-        if (clientTemplate != null) {
+        if (clientTemplate != null && client.useTemplateMappers()) {
             for (ProtocolMapperModel protocolMapper : clientTemplate.getProtocolMappers()) {
                 if (protocolMapper.getProtocol().equals(clientSession.getAuthMethod())) {
                     requestedProtocolMappers.add(protocolMapper.getId());
@@ -322,14 +322,22 @@ public class TokenManager {
         }
 
 
+        ClientTemplateModel template = client.getClientTemplate();
 
-        if (client.isFullScopeAllowed()) {
+        boolean useTemplateScope = template != null && client.useTemplateScope();
+
+        if ( (useTemplateScope && template.isFullScopeAllowed()) || (client.isFullScopeAllowed())) {
+            logger.debug("Using full scope for client");
             requestedRoles = roleMappings;
         } else {
-
-            Set<RoleModel> scopeMappings = client.getScopeMappings();
+            Set<RoleModel> scopeMappings = new HashSet<>();
+            if (useTemplateScope) {
+                logger.debug("Adding template scope mappings");
+                scopeMappings.addAll(template.getScopeMappings());
+            }
             scopeMappings.addAll(client.getRoles());
-
+            Set<RoleModel> clientScopeMappings = client.getScopeMappings();
+            scopeMappings.addAll(clientScopeMappings);
             for (RoleModel role : roleMappings) {
                 for (RoleModel desiredRole : scopeMappings) {
                     Set<RoleModel> visited = new HashSet<RoleModel>();
@@ -337,7 +345,6 @@ public class TokenManager {
                 }
             }
         }
-
         if (applyScopeParam) {
             Collection<String> scopeParamRoles;
             if (scopeParam != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
index 509d485..53cc76b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
@@ -67,7 +67,7 @@ public class ClientTemplateResource {
     protected RealmModel realm;
     private RealmAuth auth;
     private AdminEventBuilder adminEvent;
-    protected ClientTemplateModel client;
+    protected ClientTemplateModel template;
     protected KeycloakSession session;
 
     @Context
@@ -80,10 +80,10 @@ public class ClientTemplateResource {
         return keycloak;
     }
 
-    public ClientTemplateResource(RealmModel realm, RealmAuth auth, ClientTemplateModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) {
+    public ClientTemplateResource(RealmModel realm, RealmAuth auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
-        this.client = clientModel;
+        this.template = template;
         this.session = session;
         this.adminEvent = adminEvent;
 
@@ -92,12 +92,22 @@ public class ClientTemplateResource {
 
     @Path("protocol-mappers")
     public ProtocolMappersResource getProtocolMappers() {
-        ProtocolMappersResource mappers = new ProtocolMappersResource(client, auth, adminEvent);
+        ProtocolMappersResource mappers = new ProtocolMappersResource(template, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(mappers);
         return mappers;
     }
 
     /**
+     * Base path for managing the scope mappings for the client
+     *
+     * @return
+     */
+    @Path("scope-mappings")
+    public ScopeMappedResource getScopeMappedResource() {
+        return new ScopeMappedResource(realm, auth, template, session, adminEvent);
+    }
+
+    /**
      * Update the client template
      * @param rep
      * @return
@@ -108,7 +118,7 @@ public class ClientTemplateResource {
         auth.requireManage();
 
         try {
-            RepresentationToModel.updateClientTemplate(rep, client);
+            RepresentationToModel.updateClientTemplate(rep, template);
             adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
@@ -127,7 +137,7 @@ public class ClientTemplateResource {
     @Produces(MediaType.APPLICATION_JSON)
     public ClientTemplateRepresentation getClient() {
         auth.requireView();
-        return ModelToRepresentation.toRepresentation(client);
+        return ModelToRepresentation.toRepresentation(template);
     }
 
     /**
@@ -138,7 +148,7 @@ public class ClientTemplateResource {
     @NoCache
     public void deleteClientTemplate() {
         auth.requireManage();
-        realm.removeClientTemplate(client.getId());
+        realm.removeClientTemplate(template.getId());
         adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
index 44b355e..b90cb0f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
@@ -7,6 +7,8 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.ScopeContainerModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 
@@ -29,15 +31,15 @@ import java.util.Set;
 public class ScopeMappedClientResource {
     protected RealmModel realm;
     private RealmAuth auth;
-    protected ClientModel client;
+    protected ScopeContainerModel scopeContainer;
     protected KeycloakSession session;
     protected ClientModel scopedClient;
     protected AdminEventBuilder adminEvent;
     
-    public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) {
+    public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
-        this.client = client;
+        this.scopeContainer = scopeContainer;
         this.session = session;
         this.scopedClient = scopedClient;
         this.adminEvent = adminEvent;
@@ -56,7 +58,7 @@ public class ScopeMappedClientResource {
     public List<RoleRepresentation> getClientScopeMappings() {
         auth.requireView();
 
-        Set<RoleModel> mappings = scopedClient.getClientScopeMappings(client);
+        Set<RoleModel> mappings = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer); //scopedClient.getClientScopeMappings(client);
         List<RoleRepresentation> mapRep = new ArrayList<RoleRepresentation>();
         for (RoleModel roleModel : mappings) {
             mapRep.add(ModelToRepresentation.toRepresentation(roleModel));
@@ -79,7 +81,7 @@ public class ScopeMappedClientResource {
         auth.requireView();
 
         Set<RoleModel> roles = scopedClient.getRoles();
-        return ScopeMappedResource.getAvailable(client, roles);
+        return ScopeMappedResource.getAvailable(scopeContainer, roles);
     }
 
     /**
@@ -97,7 +99,7 @@ public class ScopeMappedClientResource {
         auth.requireView();
 
         Set<RoleModel> roles = scopedClient.getRoles();
-        return ScopeMappedResource.getComposite(client, roles);
+        return ScopeMappedResource.getComposite(scopeContainer, roles);
     }
 
     /**
@@ -115,7 +117,7 @@ public class ScopeMappedClientResource {
             if (roleModel == null) {
                 throw new NotFoundException("Role not found");
             }
-            client.addScopeMapping(roleModel);
+            scopeContainer.addScopeMapping(roleModel);
             adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri(), roleModel.getId()).representation(roles).success();
         }
     }
@@ -131,9 +133,9 @@ public class ScopeMappedClientResource {
         auth.requireManage();
 
         if (roles == null) {
-            Set<RoleModel> roleModels = scopedClient.getClientScopeMappings(client);
+            Set<RoleModel> roleModels = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer);//scopedClient.getClientScopeMappings(client);
             for (RoleModel roleModel : roleModels) {
-                client.deleteScopeMapping(roleModel);
+                scopeContainer.deleteScopeMapping(roleModel);
             }
             adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).representation(roles).success();
         } else {
@@ -142,7 +144,7 @@ public class ScopeMappedClientResource {
                 if (roleModel == null) {
                     throw new NotFoundException("Role not found");
                 }
-                client.deleteScopeMapping(roleModel);
+                scopeContainer.deleteScopeMapping(roleModel);
                 adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri(), roleModel.getId()).representation(roles).success();
             }
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
index 2d9b6a2..499392c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
@@ -7,6 +7,8 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.ScopeContainerModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.ClientMappingsRepresentation;
 import org.keycloak.representations.idm.MappingsRepresentation;
@@ -36,14 +38,14 @@ import java.util.Set;
 public class ScopeMappedResource {
     protected RealmModel realm;
     private RealmAuth auth;
-    protected ClientModel client;
+    protected ScopeContainerModel scopeContainer;
     protected KeycloakSession session;
     protected AdminEventBuilder adminEvent;
 
-    public ScopeMappedResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, AdminEventBuilder adminEvent) {
+    public ScopeMappedResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, AdminEventBuilder adminEvent) {
         this.realm = realm;
         this.auth = auth;
-        this.client = client;
+        this.scopeContainer = scopeContainer;
         this.session = session;
         this.adminEvent = adminEvent;
     }
@@ -60,7 +62,7 @@ public class ScopeMappedResource {
         auth.requireView();
 
         MappingsRepresentation all = new MappingsRepresentation();
-        Set<RoleModel> realmMappings = client.getRealmScopeMappings();
+        Set<RoleModel> realmMappings = scopeContainer.getRealmScopeMappings();
         if (realmMappings.size() > 0) {
             List<RoleRepresentation> realmRep = new ArrayList<RoleRepresentation>();
             for (RoleModel roleModel : realmMappings) {
@@ -73,7 +75,7 @@ public class ScopeMappedResource {
         if (clients.size() > 0) {
             Map<String, ClientMappingsRepresentation> clientMappings = new HashMap<String, ClientMappingsRepresentation>();
             for (ClientModel client : clients) {
-                Set<RoleModel> roleMappings = client.getClientScopeMappings(this.client);
+                Set<RoleModel> roleMappings = KeycloakModelUtils.getClientScopeMappings(client, this.scopeContainer); //client.getClientScopeMappings(this.client);
                 if (roleMappings.size() > 0) {
                     ClientMappingsRepresentation mappings = new ClientMappingsRepresentation();
                     mappings.setId(client.getId());
@@ -103,7 +105,7 @@ public class ScopeMappedResource {
     public List<RoleRepresentation> getRealmScopeMappings() {
         auth.requireView();
 
-        Set<RoleModel> realmMappings = client.getRealmScopeMappings();
+        Set<RoleModel> realmMappings = scopeContainer.getRealmScopeMappings();
         List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
         for (RoleModel roleModel : realmMappings) {
             realmMappingsRep.add(ModelToRepresentation.toRepresentation(roleModel));
@@ -124,10 +126,10 @@ public class ScopeMappedResource {
         auth.requireView();
 
         Set<RoleModel> roles = realm.getRoles();
-        return getAvailable(client, roles);
+        return getAvailable(scopeContainer, roles);
     }
 
-    public static List<RoleRepresentation> getAvailable(ClientModel client, Set<RoleModel> roles) {
+    public static List<RoleRepresentation> getAvailable(ScopeContainerModel client, Set<RoleModel> roles) {
         List<RoleRepresentation> available = new ArrayList<RoleRepresentation>();
         for (RoleModel roleModel : roles) {
             if (client.hasScope(roleModel)) continue;
@@ -153,10 +155,10 @@ public class ScopeMappedResource {
         auth.requireView();
 
         Set<RoleModel> roles = realm.getRoles();
-        return getComposite(client, roles);
+        return getComposite(scopeContainer, roles);
     }
 
-    public static List<RoleRepresentation> getComposite(ClientModel client, Set<RoleModel> roles) {
+    public static List<RoleRepresentation> getComposite(ScopeContainerModel client, Set<RoleModel> roles) {
         List<RoleRepresentation> composite = new ArrayList<RoleRepresentation>();
         for (RoleModel roleModel : roles) {
             if (client.hasScope(roleModel)) composite.add(ModelToRepresentation.toRepresentation(roleModel));
@@ -180,7 +182,7 @@ public class ScopeMappedResource {
             if (roleModel == null) {
                 throw new NotFoundException("Role not found");
             }
-            client.addScopeMapping(roleModel);
+            scopeContainer.addScopeMapping(roleModel);
             adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri(), role.getId()).representation(roles).success();
         }
     }
@@ -197,9 +199,9 @@ public class ScopeMappedResource {
         auth.requireManage();
 
         if (roles == null) {
-            Set<RoleModel> roleModels = client.getRealmScopeMappings();
+            Set<RoleModel> roleModels = scopeContainer.getRealmScopeMappings();
             for (RoleModel roleModel : roleModels) {
-                client.deleteScopeMapping(roleModel);
+                scopeContainer.deleteScopeMapping(roleModel);
             }
             adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).representation(roles).success();
        } else {
@@ -208,7 +210,7 @@ public class ScopeMappedResource {
                 if (roleModel == null) {
                     throw new NotFoundException("Client not found");
                 }
-                client.deleteScopeMapping(roleModel);
+                scopeContainer.deleteScopeMapping(roleModel);
                 adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri(), roleModel.getId()).representation(roles).success();
             }
         }
@@ -221,6 +223,6 @@ public class ScopeMappedResource {
         if (clientModel == null) {
             throw new NotFoundException("Client not found");
         }
-        return new ScopeMappedClientResource(realm, auth, this.client, session, clientModel, adminEvent);
+        return new ScopeMappedClientResource(realm, auth, this.scopeContainer, session, clientModel, adminEvent);
     }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index d02c255..ab76f89 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -168,7 +168,7 @@ public class AccountTest {
         });
     }
 
-    //@Test
+    @Test
     public void ideTesting() throws Exception {
         Thread.sleep(100000000);
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index f016ff0..57afc83 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -25,6 +25,7 @@ import org.keycloak.models.UserFederationProviderFactory;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
@@ -191,7 +192,7 @@ public class ImportTest extends AbstractModelTest {
         Set<RoleModel> realmScopes = oauthClient.getRealmScopeMappings();
         Assert.assertTrue(realmScopes.contains(realm.getRole("admin")));
 
-        Set<RoleModel> appScopes = application.getClientScopeMappings(oauthClient);
+        Set<RoleModel> appScopes = KeycloakModelUtils.getClientScopeMappings(application, oauthClient);//application.getClientScopeMappings(oauthClient);
         Assert.assertTrue(appScopes.contains(application.getRole("app-user")));
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 8473540..5977916 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -62,6 +62,8 @@ import org.keycloak.representations.IDToken;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.ClientTemplateRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
@@ -803,6 +805,25 @@ public class AccessTokenTest {
     @Test
     public void testClientTemplate() throws Exception {
         RealmResource realm = keycloak.realms().realm("test");
+        RoleRepresentation realmRole = new RoleRepresentation();
+        realmRole.setName("realm-test-role");
+        realm.roles().create(realmRole);
+        realmRole = realm.roles().get("realm-test-role").toRepresentation();
+        RoleRepresentation realmRole2 = new RoleRepresentation();
+        realmRole2.setName("realm-test-role2");
+        realm.roles().create(realmRole2);
+        realmRole2 = realm.roles().get("realm-test-role2").toRepresentation();
+
+
+        List<UserRepresentation> users = realm.users().search("test-user@localhost", -1, -1);
+        Assert.assertEquals(1, users.size());
+        UserRepresentation user = users.get(0);
+
+        List<RoleRepresentation> addRoles = new LinkedList<>();
+        addRoles.add(realmRole);
+        addRoles.add(realmRole2);
+        realm.users().get(user.getId()).roles().realmLevel().add(addRoles);
+
         ClientTemplateRepresentation rep = new ClientTemplateRepresentation();
         rep.setName("template");
         rep.setProtocol("oidc");
@@ -825,8 +846,8 @@ public class AccessTokenTest {
             }
 
         }
-        Assert.assertNotNull(clientRep);
         clientRep.setClientTemplate("template");
+        clientRep.setFullScopeAllowed(false);
         realm.clients().get(clientRep.getId()).update(clientRep);
 
         {
@@ -844,13 +865,150 @@ public class AccessTokenTest {
             AccessToken accessToken = getAccessToken(tokenResponse);
             Assert.assertEquals("coded", accessToken.getOtherClaims().get("hard"));
 
+            // check zero scope for template
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
+
+            response.close();
+            client.close();
+        }
+
+        // test that scope is added
+        List<RoleRepresentation> addRole1 = new LinkedList<>();
+        addRole1.add(realmRole);
+        templateResource.getScopeMappings().realmLevel().add(addRole1);
+
+        {
+            Client client = ClientBuilder.newClient();
+            UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            WebTarget grantTarget = client.target(grantUri);
+
+            response = executeGrantAccessTokenRequest(grantTarget);
+            Assert.assertEquals(200, response.getStatus());
+            org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
+            AccessToken accessToken = getAccessToken(tokenResponse);
+            // check zero scope for template
+            Assert.assertNotNull(accessToken.getRealmAccess());
+            Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
 
             response.close();
             client.close();
         }
+
+        // test combined scopes
+        List<RoleRepresentation> addRole2 = new LinkedList<>();
+        addRole2.add(realmRole2);
+        realm.clients().get(clientRep.getId()).getScopeMappings().realmLevel().add(addRole2);
+
+        {
+            Client client = ClientBuilder.newClient();
+            UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            WebTarget grantTarget = client.target(grantUri);
+
+            response = executeGrantAccessTokenRequest(grantTarget);
+            Assert.assertEquals(200, response.getStatus());
+            org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
+
+            AccessToken accessToken = getAccessToken(tokenResponse);
+
+            // check zero scope for template
+            Assert.assertNotNull(accessToken.getRealmAccess());
+            Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
+
+            response.close();
+            client.close();
+        }
+
+        // remove scopes and retest
+        templateResource.getScopeMappings().realmLevel().remove(addRole1);
+        realm.clients().get(clientRep.getId()).getScopeMappings().realmLevel().remove(addRole2);
+
+        {
+            Client client = ClientBuilder.newClient();
+            UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            WebTarget grantTarget = client.target(grantUri);
+
+            response = executeGrantAccessTokenRequest(grantTarget);
+            Assert.assertEquals(200, response.getStatus());
+            org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
+
+            AccessToken accessToken = getAccessToken(tokenResponse);
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
+
+            response.close();
+            client.close();
+        }
+
+        // test full scope on template
+        rep.setFullScopeAllowed(true);
+        templateResource.update(rep);
+
+        {
+            Client client = ClientBuilder.newClient();
+            UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            WebTarget grantTarget = client.target(grantUri);
+
+            response = executeGrantAccessTokenRequest(grantTarget);
+            Assert.assertEquals(200, response.getStatus());
+            org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
+
+            AccessToken accessToken = getAccessToken(tokenResponse);
+
+            // check zero scope for template
+            Assert.assertNotNull(accessToken.getRealmAccess());
+            Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
+
+            response.close();
+            client.close();
+        }
+
+        // test don't use template scope
+        clientRep.setUseTemplateScope(false);
+        realm.clients().get(clientRep.getId()).update(clientRep);
+
+        {
+            Client client = ClientBuilder.newClient();
+            UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            WebTarget grantTarget = client.target(grantUri);
+
+            response = executeGrantAccessTokenRequest(grantTarget);
+            Assert.assertEquals(200, response.getStatus());
+            org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
+
+            AccessToken accessToken = getAccessToken(tokenResponse);
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
+            Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+
+
+            response.close();
+            client.close();
+        }
+
+
+
+
         // undo mappers
         clientRep.setClientTemplate(ClientTemplateRepresentation.NONE);
+        clientRep.setFullScopeAllowed(true);
         realm.clients().get(clientRep.getId()).update(clientRep);
+        realm.users().get(user.getId()).roles().realmLevel().remove(addRoles);
+        realm.roles().get(realmRole.getName()).remove();
+        realm.roles().get(realmRole2.getName()).remove();
+        templateResource.remove();
 
         {
             Client client = ClientBuilder.newClient();