Merge pull request #1441 from mposolda/master
KEYCLOAK-1233 Admin console support for add/remove federated identity
This commit is contained in:
commit
af922920ab
12 changed files with 194 additions and 54 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 : {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1,45 @@
|
|||
<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>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="4">
|
||||
<div class="form-inline">
|
||||
<div class="pull-right" data-ng-show="hasAnyProvidersToCreate()">
|
||||
<a class="btn btn-primary" href="#/create/federated-identity/{{realm.realm}}/{{user.id}}">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr data-ng-hide="federatedIdentities.length == 0">
|
||||
<th>Identity Provider Alias</th>
|
||||
<th>Provider user ID</th>
|
||||
<th>Provider username</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-repeat="identity in federatedIdentities">
|
||||
<td>{{identity.identityProvider}}</td>
|
||||
<td>{{identity.userId}}</td>
|
||||
<td>{{identity.userName}}</td>
|
||||
<td class="actions">
|
||||
<div class="action-div"><i class="pficon pficon-delete" ng-click="removeProviderLink(identity)" tooltip-placement="right" tooltip="Remove Provider Link"></i></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="federatedIdentities.length == 0">
|
||||
<td>No identity provider links available</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -1,27 +0,0 @@
|
|||
<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>{{user.username|capitalize}}</h1>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Identity Provider Alias</th>
|
||||
<th>Username</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-repeat="identity in federatedIdentities">
|
||||
<td>{{identity.identityProvider}}</td>
|
||||
<td>{{identity.userName}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -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>
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue