Skip to content

Commit

Permalink
Merge pull request keycloak#1441 from mposolda/master
Browse files Browse the repository at this point in the history
KEYCLOAK-1233 Admin console support for add/remove federated identity
  • Loading branch information
mposolda committed Jul 11, 2015
2 parents aeab25b + 142d44a commit af92292
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 54 deletions.
2 changes: 1 addition & 1 deletion docbook/reference/en/en-US/modules/kerberos.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docbook/reference/en/en-US/modules/user-federation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,8 @@ public UserRepresentation getUser(final @PathParam("id") String id) {
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())) {
Expand Down Expand Up @@ -318,6 +312,10 @@ public List<FederatedIdentityRepresentation> getFederatedIdentity(final @PathPar
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>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
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();
}
Expand Down

0 comments on commit af92292

Please sign in to comment.