keycloak-aplcache

Details

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>();