commit
32e069eadc
10 changed files with 224 additions and 7 deletions
|
@ -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;
|
||||
|
|
31
docbook/auth-server-docs/reference/en/en-US/modules/groups.xml
Executable file
31
docbook/auth-server-docs/reference/en/en-US/modules/groups.xml
Executable file
|
@ -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>
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -1559,4 +1559,15 @@ module.factory('UserGroupMapping', function($resource) {
|
|||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('DefaultGroups', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/default-groups/:groupId', {
|
||||
realm : '@realm',
|
||||
groupId : '@groupId'
|
||||
}, {
|
||||
update : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue