keycloak-aplcache
Changes
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js 58(+57 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity-detail.html 50(+50 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity-list.html 34(+26 -8)
Details
diff --git a/docbook/reference/en/en-US/modules/kerberos.xml b/docbook/reference/en/en-US/modules/kerberos.xml
index 3a858a1..d262a1d 100644
--- a/docbook/reference/en/en-US/modules/kerberos.xml
+++ b/docbook/reference/en/en-US/modules/kerberos.xml
@@ -216,7 +216,7 @@ ktadd -k /tmp/http.keytab HTTP/www.mydomain.org@MYDOMAIN.ORG
GSS credential will need to be used by your application. So you need to enable built-in <literal>gss delegation credential</literal> protocol mapper
in admin console for your application. This will cause that Keycloak will deserialize GSS credential and transmit it to the application
in access token. Application will need to deserialize it and use it for further GSS calls against other services. We have an example, which is showing it in details. It's in <literal>examples/kerberos</literal>
- in the Keycloak appliance distribution or WAR distribution download. You can also check the example sources directly <ulink url="https://github.com/keycloak/keycloak/blob/master/examples/kerberos">here</ulink> .
+ in the Keycloak example distribution or demo distribution download. You can also check the example sources directly <ulink url="https://github.com/keycloak/keycloak/blob/master/examples/kerberos">here</ulink> .
</para>
<para>
Once you deserialize the credential from the access token to the GSSCredential object, then GSSContext will need to
diff --git a/docbook/reference/en/en-US/modules/user-federation.xml b/docbook/reference/en/en-US/modules/user-federation.xml
index c8e9856..58a8727 100755
--- a/docbook/reference/en/en-US/modules/user-federation.xml
+++ b/docbook/reference/en/en-US/modules/user-federation.xml
@@ -208,6 +208,10 @@
more attribute mappings (For example to street, postalCode etc), delete firstName/lastname mapper and put fullName mapper instead, add role mappers etc.
Admin console provides tooltips, which should help on how to configure corresponding mappers.
</para>
+ <para>
+ We have an example, which is showing LDAP integration and set of base mappers and sample mappers (mappers for street and postalCode) . It's in <literal>examples/ldap</literal>
+ in the Keycloak example distribution or demo distribution download. You can also check the example sources directly <ulink url="https://github.com/keycloak/keycloak/blob/master/examples/ldap">here</ulink> .
+ </para>
</section>
<section>
<title>Writing your own User Federation Provider</title>
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 f205ab0..aafb174 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
@@ -388,7 +388,7 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'UserSessionsCtrl'
})
.when('/realms/:realm/users/:user/federated-identity', {
- templateUrl : resourceUrl + '/partials/user-federated-identity.html',
+ templateUrl : resourceUrl + '/partials/user-federated-identity-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
@@ -402,6 +402,21 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'UserFederatedIdentityCtrl'
})
+ .when('/create/federated-identity/:realm/:user', {
+ templateUrl : resourceUrl + '/partials/user-federated-identity-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ user : function(UserLoader) {
+ return UserLoader();
+ },
+ federatedIdentities : function(UserFederatedIdentityLoader) {
+ return UserFederatedIdentityLoader();
+ }
+ },
+ controller : 'UserFederatedIdentityAddCtrl'
+ })
.when('/realms/:realm/users/:user/consents', {
templateUrl : resourceUrl + '/partials/user-consents.html',
resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 93e321b..55f6788 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -129,10 +129,66 @@ module.controller('UserSessionsCtrl', function($scope, realm, user, sessions, Us
}
});
-module.controller('UserFederatedIdentityCtrl', function($scope, realm, user, federatedIdentities) {
+module.controller('UserFederatedIdentityCtrl', function($scope, $location, realm, user, federatedIdentities, UserFederatedIdentity, Notifications, Dialog) {
$scope.realm = realm;
$scope.user = user;
$scope.federatedIdentities = federatedIdentities;
+
+ $scope.hasAnyProvidersToCreate = function() {
+ return realm.identityProviders.length - $scope.federatedIdentities.length > 0;
+ }
+
+ $scope.removeProviderLink = function(providerLink) {
+
+ console.log("Removing provider link: " + providerLink.identityProvider);
+
+ Dialog.confirmDelete(providerLink.identityProvider, 'Identity Provider Link', function() {
+ UserFederatedIdentity.remove({ realm: realm.realm, user: user.id, provider: providerLink.identityProvider }, function() {
+ Notifications.success("The provider link has been deleted.");
+ var indexToRemove = $scope.federatedIdentities.indexOf(providerLink);
+ $scope.federatedIdentities.splice(indexToRemove, 1);
+ });
+ });
+ }
+});
+
+module.controller('UserFederatedIdentityAddCtrl', function($scope, $location, realm, user, federatedIdentities, UserFederatedIdentity, Notifications) {
+ $scope.realm = realm;
+ $scope.user = user;
+ $scope.federatedIdentity = {};
+
+ var getAvailableProvidersToCreate = function() {
+ var realmProviders = [];
+ for (var i=0 ; i<realm.identityProviders.length ; i++) {
+ var providerAlias = realm.identityProviders[i].alias;
+ realmProviders.push(providerAlias);
+ };
+
+ for (var i=0 ; i<federatedIdentities.length ; i++) {
+ var providerAlias = federatedIdentities[i].identityProvider;
+ var index = realmProviders.indexOf(providerAlias);
+ realmProviders.splice(index, 1);
+ }
+
+ return realmProviders;
+ }
+ $scope.availableProvidersToCreate = getAvailableProvidersToCreate();
+
+ $scope.save = function() {
+ UserFederatedIdentity.save({
+ realm : realm.realm,
+ user: user.id,
+ provider: $scope.federatedIdentity.identityProvider
+ }, $scope.federatedIdentity, function(data, headers) {
+ $location.url("/realms/" + realm.realm + '/users/' + $scope.user.id + '/federated-identity');
+ Notifications.success("Provider link has been created.");
+ });
+ };
+
+ $scope.cancel = function() {
+ $location.url("/realms/" + realm.realm + '/users/' + $scope.user.id + '/federated-identity');
+ };
+
});
module.controller('UserConsentsCtrl', function($scope, realm, user, userConsents, UserConsents, Notifications) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index 72c6b8c..773f6f0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -171,8 +171,8 @@ module.factory('UserSessionsLoader', function(Loader, UserSessions, $route, $q)
});
});
-module.factory('UserFederatedIdentityLoader', function(Loader, UserFederatedIdentity, $route, $q) {
- return Loader.query(UserFederatedIdentity, function() {
+module.factory('UserFederatedIdentityLoader', function(Loader, UserFederatedIdentities, $route, $q) {
+ return Loader.query(UserFederatedIdentities, function() {
return {
realm : $route.current.params.realm,
user : $route.current.params.user
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 3107101..3f79536 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
@@ -305,12 +305,21 @@ module.factory('UserLogout', function($resource) {
user : '@user'
});
});
-module.factory('UserFederatedIdentity', function($resource) {
+
+module.factory('UserFederatedIdentities', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:user/federated-identity', {
realm : '@realm',
user : '@user'
});
});
+module.factory('UserFederatedIdentity', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/users/:user/federated-identity/:provider', {
+ realm : '@realm',
+ user : '@user',
+ provider : '@provider'
+ });
+});
+
module.factory('UserConsents', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:user/consents/:client', {
realm : '@realm',
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity-detail.html
new file mode 100644
index 0000000..a9155fe
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity-detail.html
@@ -0,0 +1,50 @@
+<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}}/users">Users</a></li>
+ <li>{{user.username}}</li>
+ </ol>
+
+ <h1 data-ng-show="create">Add Identity Provider Link</h1>
+
+ <kc-tabs-user></kc-tabs-user>
+
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+ <fieldset>
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="identityProvider">Identity Provier <span class="required">*</span></label>
+ <div class="col-sm-6">
+ <div>
+ <select class="form-control" id="identityProvider"
+ ng-model="federatedIdentity.identityProvider"
+ ng-options="providerAlias for providerAlias in availableProvidersToCreate"
+ required>
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-md-2 control-label" for="userId">Identity Provider User ID <span class="required">*</span></label>
+ <div class="col-md-6">
+ <input class="form-control" id="userId" type="text" ng-model="federatedIdentity.userId" required>
+ </div>
+ <kc-tooltip>Unique ID of the user on the Identity Provider side</kc-tooltip>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-md-2 control-label" for="userName">Identity Provider Username <span class="required">*</span></label>
+ <div class="col-md-6">
+ <input class="form-control" id="userName" type="text" ng-model="federatedIdentity.userName" required>
+ </div>
+ <kc-tooltip>Username on the Identity Provider side</kc-tooltip>
+ </div>
+
+ </fieldset>
+
+ <div class="pull-right form-actions" data-ng-show="access.manageRealm">
+ <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save>Save</button>
+ </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-user.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
index 0b0a499..caee5c1 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
@@ -1,8 +1,8 @@
<ul class="nav nav-tabs" data-ng-show="!create">
- <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Attributes</a></li>
+ <li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Attributes</a></li>
<li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">Credentials</a></li>
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">Role Mappings</a></li>
<li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">Consents</a></li>
<li ng-class="{active: path[4] == 'sessions'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/sessions">Sessions</a></li>
- <li ng-class="{active: path[4] == 'federated-identity'}" data-ng-show="user.federatedIdentities && user.federatedIdentities.length > 0"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/federated-identity">Identity Provider Links</a></li>
+ <li ng-class="{active: path[4] == 'federated-identity' || path[1] == 'federated-identity'}" data-ng-show="user.federatedIdentities != null"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/federated-identity">Identity Provider Links</a></li>
</ul>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index b6f1fb1..0b56278 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -259,14 +259,8 @@ public class UsersResource {
UserRepresentation rep = ModelToRepresentation.toRepresentation(user);
if (realm.isIdentityFederationEnabled()) {
- Set<FederatedIdentityModel> identities = session.users().getFederatedIdentities(user, realm);
- if (!identities.isEmpty()) {
- List<FederatedIdentityRepresentation> reps = new LinkedList<>();
- for (FederatedIdentityModel m : identities) {
- reps.add(ModelToRepresentation.toRepresentation(m));
- }
- rep.setFederatedIdentities(reps);
- }
+ List<FederatedIdentityRepresentation> reps = getFederatedIdentities(user);
+ rep.setFederatedIdentities(reps);
}
if ((protector != null) && protector.isTemporarilyDisabled(session, realm, rep.getUsername())) {
@@ -318,6 +312,10 @@ public class UsersResource {
throw new NotFoundException("User not found");
}
+ return getFederatedIdentities(user);
+ }
+
+ private List<FederatedIdentityRepresentation> getFederatedIdentities(UserModel user) {
Set<FederatedIdentityModel> identities = session.users().getFederatedIdentities(user, realm);
List<FederatedIdentityRepresentation> result = new ArrayList<FederatedIdentityRepresentation>();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPExampleServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPExampleServlet.java
index a629ddb..80bc9f0 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPExampleServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPExampleServlet.java
@@ -36,17 +36,7 @@ public class LDAPExampleServlet extends HttpServlet {
out.println();
for (Map.Entry<String, Object> claim : idToken.getOtherClaims().entrySet()) {
- Object value = claim.getValue();
-
- if (value instanceof List) {
- List<String> asList = (List<String>) value;
- StringBuilder result = new StringBuilder();
- for (String item : asList) {
- result.append(item + "<br>");
- }
- value = result.toString();
- }
-
+ String value = claim.getValue().toString();
out.printf("<tr><td>%s</td><td>%s</td></tr>", claim.getKey(), value);
out.println();
}