keycloak-uncached

Details

diff --git a/docbook/auth-server-docs/reference/en/en-US/master.xml b/docbook/auth-server-docs/reference/en/en-US/master.xml
index 95ff5ee..16a8601 100755
--- a/docbook/auth-server-docs/reference/en/en-US/master.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/master.xml
@@ -27,6 +27,7 @@
                 <!ENTITY Migration SYSTEM "modules/MigrationFromOlderVersions.xml">
                 <!ENTITY Email SYSTEM "modules/email.xml">
                 <!ENTITY Roles SYSTEM "modules/roles.xml">
+                <!ENTITY Groups SYSTEM "modules/groups.xml">
                 <!ENTITY DirectAccess SYSTEM "modules/direct-access.xml">
                 <!ENTITY ServiceAccounts SYSTEM "modules/service-accounts.xml">
                 <!ENTITY CORS SYSTEM "modules/cors.xml">
@@ -133,6 +134,7 @@ This one is short
     </chapter>
     &AccessTypes;
     &Roles;
+    &Groups;
     &DirectAccess;
     &ServiceAccounts;
     &CORS;
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/groups.xml b/docbook/auth-server-docs/reference/en/en-US/modules/groups.xml
new file mode 100755
index 0000000..eb54e0e
--- /dev/null
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/groups.xml
@@ -0,0 +1,31 @@
+<chapter id="groups">
+    <title>Groups</title>
+    <para>
+        Groups in Keycloak allow you to manage a common set of attributes and role mappings for a large set of users.
+        Users can be members of zero or more groups.  Users inherit the attributes and role mappings assign to each group.
+        As an admin this makes it easy for you to manage permissions for a user in one place.
+    </para>
+    <para>
+        Groups are hierarchical.  A group can have many subgroups, but a group can only have one parent.  Subgroups inherit
+        the attributes and role mappings from the parent.  This applies to user as well.  So, if you have a parent group and a child group
+        and a user that only belongs to the child group, the user inherits the attributes and role mappings of both the
+        parent and child.
+    </para>
+    <section>
+        <title>Groups vs. Roles</title>
+        <para>
+            In the IT world the concepts of Group and Role are often blurred and interchangeable.  In Keycloak, Groups are just
+            a collection of users that you can apply roles and attributes to in one place.  Roles are used to assign permissions
+            and access control.
+        </para>
+        <para>
+            Keycloak Roles have the concept of a Composite Role.  A role can be associated with one or more additional roles.
+            This is called a Composite Role.  If a user has a role mapping to the Composite Role, they inherit all the roles associated
+            with the composite.  So what's the difference from a Keycloak Group and a Composite Role?  Logically they could be
+            used for the same exact thing.  The difference is conceptual.  Composite roles should be used to compose the
+            permission model of your set of services and applications.  So, roles become a set of permissions.  Groups on the
+            other hand, would be a set of users that have a set of permissions.  Use Groups to manage users, composite roles to
+            manage applications and services.
+        </para>
+    </section>
+</chapter>
\ No newline at end of file
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/roles.xml b/docbook/auth-server-docs/reference/en/en-US/modules/roles.xml
index 9cd6176..b7c034c 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/roles.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/roles.xml
@@ -1,7 +1,7 @@
 <chapter id="roles">
     <title>Roles</title>
     <para>
-        In Keycloak, roles (or permissions) can be defined globally at the realm level, or individually per application.
+        In Keycloak, roles can be defined globally at the realm level, or individually per application.
         Each role has a name which must be unique at the level it is defined in, i.e. you can have only one "admin" role at
         the realm level.  You may have that a role named "admin" within an Application too, but "admin" must be unique
         for that application.
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 9972eb1..bfbcee9 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
@@ -697,6 +697,18 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'GroupRoleMappingCtrl'
         })
+        .when('/realms/:realm/default-groups', {
+            templateUrl : resourceUrl + '/partials/default-groups.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                groups : function(GroupListLoader) {
+                    return GroupListLoader();
+                }
+            },
+            controller : 'DefaultGroupsCtrl'
+        })
 
 
         .when('/create/role/:realm/clients/:client', {
@@ -1995,6 +2007,15 @@ module.directive('kcTabsGroup', function () {
     }
 });
 
+module.directive('kcTabsGroupList', function () {
+    return {
+        scope: true,
+        restrict: 'E',
+        replace: true,
+        templateUrl: resourceUrl + '/templates/kc-tabs-group-list.html'
+    }
+});
+
 module.directive('kcTabsClient', function () {
     return {
         scope: true,
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
index 93070ba..c336dc8 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
@@ -366,3 +366,63 @@ module.controller('GroupMembersCtrl', function($scope, realm, group, GroupMember
 
 });
 
+module.controller('DefaultGroupsCtrl', function($scope, $route, realm, groups, DefaultGroups, Notifications, $location, Dialog) {
+    $scope.realm = realm;
+    $scope.groupList = groups;
+    $scope.selectedGroup = null;
+    $scope.tree = [];
+
+    DefaultGroups.query({realm: realm.realm}, function(data) {
+        $scope.defaultGroups = data;
+
+    });
+
+    $scope.addDefaultGroup = function() {
+        if (!$scope.tree.currentNode) {
+            Notifications.error('Please select a group to add');
+            return;
+        };
+
+        DefaultGroups.update({realm: realm.realm, groupId: $scope.tree.currentNode.id}, function() {
+            Notifications.success('Added default group');
+            $route.reload();
+        });
+
+    };
+
+    $scope.removeDefaultGroup = function() {
+        DefaultGroups.remove({realm: realm.realm, groupId: $scope.selectedGroup.id}, function() {
+            Notifications.success('Removed default group');
+            $route.reload();
+        });
+
+    };
+
+    var isLeaf = function(node) {
+        return node.id != "realm" && (!node.subGroups || node.subGroups.length == 0);
+    };
+
+    $scope.getGroupClass = function(node) {
+        if (node.id == "realm") {
+            return 'pficon pficon-users';
+        }
+        if (isLeaf(node)) {
+            return 'normal';
+        }
+        if (node.subGroups.length && node.collapsed) return 'collapsed';
+        if (node.subGroups.length && !node.collapsed) return 'expanded';
+        return 'collapsed';
+
+    }
+
+    $scope.getSelectedClass = function(node) {
+        if (node.selected) {
+            return 'selected';
+        } else if ($scope.cutNode && $scope.cutNode.id == node.id) {
+            return 'cut';
+        }
+        return undefined;
+    }
+
+});
+
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 15e77a4..3ca2183 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
@@ -1559,4 +1559,15 @@ module.factory('UserGroupMapping', function($resource) {
             method : 'PUT'
         }
     });
-});
\ No newline at end of file
+});
+
+module.factory('DefaultGroups', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/default-groups/:groupId', {
+        realm : '@realm',
+        groupId : '@groupId'
+    }, {
+        update : {
+            method : 'PUT'
+        }
+    });
+});
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/default-groups.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/default-groups.html
new file mode 100755
index 0000000..bcfd547
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/default-groups.html
@@ -0,0 +1,80 @@
+ <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+     <kc-tabs-group-list></kc-tabs-group-list>
+
+     <form class="form-horizontal" name="realmForm" novalidate>
+         <div class="form-group" kc-read-only="!access.manageRealm">
+             <label class="col-md-1 control-label" class="control-label"></label>
+
+             <div class="col-md-8" >
+                  <div class="row">
+                     <div class="col-md-5">
+                         <table class="table table-striped table-bordered">
+                             <thead>
+                             <tr>
+                                 <th class="kc-table-actions" colspan="5">
+                                     <div class="form-inline">
+                                         <label class="control-label">Default Groups</label>
+                                         <kc-tooltip>Newly created or registered users will automatically be added to these groups</kc-tooltip>
+
+                                         <div class="pull-right" data-ng-show="access.manageRealm">
+                                             <button id="removeDefaultGroup" class="btn btn-default" ng-click="removeDefaultGroup()">Remove</button>
+                                         </div>
+                                     </div>
+                                 </th>
+                             </tr>
+                             </thead>
+                             <tbody>
+                             <tr>
+                                 <td>
+                                     <select id="defaultGroups" class="form-control" size=5
+                                                            ng-model="selectedGroup"
+                                                            ng-options="r.path for r in defaultGroups">
+                                         <option style="display:none" value="">select a type</option>
+                                 </select>
+
+
+                                 </td>
+                             </tr>
+                             </tbody>
+                         </table>
+                     </div>
+                     <div class="col-md-5">
+                         <table class="table table-striped table-bordered">
+                             <thead>
+                             <tr>
+                                 <th class="kc-table-actions" colspan="5">
+
+                                     <div class="form-inline">
+                                         <label class="control-label">Available Groups</label>
+                                         <kc-tooltip>Select a group you want to add as a default.</kc-tooltip>
+
+                                         <div class="pull-right" data-ng-show="access.manageRealm">
+                                             <button id="addDefaultGroup" class="btn btn-default" ng-click="addDefaultGroup()">Add</button>
+                                         </div>
+                                     </div>
+                                 </th>
+                             </tr>
+                             </thead>
+                         <tbody>
+                             <tr>
+                                 <td>                             <div
+                                         tree-id="tree"
+                                         angular-treeview="true"
+                                         tree-model="groupList"
+                                         node-id="id"
+                                         node-label="name"
+                                         node-children="subGroups" >
+                                 </div>
+
+                                 </td>
+                             </tr>
+                             </tbody>
+                         </table>
+                     </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/partials/group-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html
index 14acbae..ac8267c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-list.html
@@ -1,8 +1,5 @@
  <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
-    <h1>
-        <span>User Groups</span>
-        <kc-tooltip>User groups</kc-tooltip>
-    </h1>
+     <kc-tabs-group-list></kc-tabs-group-list>
 
     <table class="table table-striped table-bordered">
          <thead>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html
new file mode 100755
index 0000000..390dce3
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html
@@ -0,0 +1,11 @@
+<div data-ng-controller="GroupTabCtrl">
+    <h1>
+        <span>User Groups</span>
+    </h1>
+
+    <ul class="nav nav-tabs">
+        <li ng-class="{active: path[2] == 'groups'}"><a href="#/realms/{{realm.realm}}/groups">Groups</a></li>
+        <li ng-class="{active: path[2] == 'default-groups'}"><a href="#/realms/{{realm.realm}}/default-groups">Default Groups</a><kc-tooltip>Set of groups that new users will automatically join.</kc-tooltip>
+        </li>
+    </ul>
+</div>
\ No newline at end of file
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index fb66a70..39f026a 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -22,6 +22,7 @@ import org.keycloak.saml.SAML2LogoutResponseBuilder;
 import org.keycloak.saml.SAMLRequestParser;
 import org.keycloak.saml.SignatureAlgorithm;
 import org.keycloak.saml.common.constants.GeneralConstants;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ProcessingException;
 import org.keycloak.saml.common.util.Base64;
 import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
@@ -33,6 +34,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
+import java.net.URI;
 import java.security.PublicKey;
 import java.security.Signature;
 import java.util.HashSet;
@@ -312,7 +314,9 @@ public abstract class SamlAuthenticator {
         }
 
 
-        final SamlPrincipal principal = new SamlPrincipal(principalName, principalName, subjectNameID.getFormat().toString(), attributes, friendlyAttributes);
+        URI nameFormat = subjectNameID.getFormat();
+        String nameFormatString = nameFormat == null ?  JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
+        final SamlPrincipal principal = new SamlPrincipal(principalName, principalName, nameFormatString, attributes, friendlyAttributes);
         String index = authn == null ? null : authn.getSessionIndex();
         final String sessionIndex = index;
         SamlSession account = new SamlSession(principal, roles, sessionIndex);