keycloak-aplcache

Details

diff --git a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
index 4e26fdf..d97e285 100755
--- a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
+++ b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -44,6 +44,28 @@ public class AdminClient {
         }
     }
 
+    public static String getContent(HttpEntity entity) throws IOException {
+        if (entity == null) return null;
+        InputStream is = entity.getContent();
+        try {
+            ByteArrayOutputStream os = new ByteArrayOutputStream();
+            int c;
+            while ((c = is.read()) != -1) {
+                os.write(c);
+            }
+            byte[] bytes = os.toByteArray();
+            String data = new String(bytes);
+            return data;
+        } finally {
+            try {
+                is.close();
+            } catch (IOException ignored) {
+
+            }
+        }
+
+    }
+
     public static AccessTokenResponse getToken(HttpServletRequest request) throws IOException {
 
         HttpClient client = new HttpClientBuilder()
@@ -64,32 +86,14 @@ public class AdminClient {
             int status = response.getStatusLine().getStatusCode();
             HttpEntity entity = response.getEntity();
             if (status != 200) {
-                throw new IOException("Bad status: " + status);
+                String json = getContent(entity);
+                throw new IOException("Bad status: " + status + " response: " + json);
             }
             if (entity == null) {
                 throw new IOException("No Entity");
             }
-            InputStream is = entity.getContent();
-            try {
-                ByteArrayOutputStream os = new ByteArrayOutputStream();
-                int c;
-                while ((c = is.read()) != -1) {
-                    os.write(c);
-                }
-                byte[] bytes = os.toByteArray();
-                String json = new String(bytes);
-                try {
-                    return JsonSerialization.readValue(json, AccessTokenResponse.class);
-                } catch (IOException e) {
-                    throw new IOException(json, e);
-                }
-            } finally {
-                try {
-                    is.close();
-                } catch (IOException ignored) {
-
-                }
-            }
+            String json = getContent(entity);
+            return JsonSerialization.readValue(json, AccessTokenResponse.class);
         } finally {
             client.getConnectionManager().shutdown();
         }
@@ -108,6 +112,8 @@ public class AdminClient {
             List<NameValuePair> formparams = new ArrayList<NameValuePair>();
             formparams.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, res.getRefreshToken()));
             formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "admin-client"));
+            UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+            post.setEntity(form);
             HttpResponse response = client.execute(post);
             boolean status = response.getStatusLine().getStatusCode() != 204;
             HttpEntity entity = response.getEntity();
diff --git a/examples/demo-template/README.md b/examples/demo-template/README.md
index 8ce7158..22a4108 100755
--- a/examples/demo-template/README.md
+++ b/examples/demo-template/README.md
@@ -15,6 +15,7 @@ machine on the network or Internet.
 * **customer-app-cli** A pure CLI application that does remote login using OAuth2 browser redirects with the auth server
 * **product-app** A WAR application that does remote login using OAuth2 browser redirects with the auth server
 * **admin-access-app** A WAR application that does remote REST login to admin console to obtain a list of realm roles from Admin REST API
+* **angular-product-app** An Angular JS pure HTML5/Javascript application.
 * **database-service** JAX-RS services authenticated by bearer tokens only. The customer and product app invoke on it to get data
 * **third-party** Simple WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
 * **third-party-cdi** Simple CDI/JSF WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
@@ -187,6 +188,24 @@ then using that token to access the Admin REST API.
 If you are already logged in, you will not be asked for a username and password, but you will be redirected to
 an oauth grant page.  This page asks you if you want to grant certain permissions to the third-part app.
 
+Step 9: Angular JS Example
+----------------------------------
+An Angular JS example using Keycloak to secure it.
+
+[http://localhost:8080/angular-product](http://localhost:8080/angular-product)
+
+If you are already logged in, you will not be asked for a username and password, but you will be redirected to
+an oauth grant page.  This page asks you if you want to grant certain permissions to the third-part app.
+
+Step 9: Pure HTML5/Javascript Example
+----------------------------------
+An pure HTML5/Javascript example using Keycloak to secure it.
+
+[http://localhost:8080/customer-portal-js](http://localhost:8080/customer-portal-js)
+
+If you are already logged in, you will not be asked for a username and password, but you will be redirected to
+an oauth grant page.  This page asks you if you want to grant certain permissions to the third-part app.
+
 Admin Console
 ==========================
 
diff --git a/examples/providers/federation-provider/README.md b/examples/providers/federation-provider/README.md
index bdb4a1b..b908fe7 100755
--- a/examples/providers/federation-provider/README.md
+++ b/examples/providers/federation-provider/README.md
@@ -3,14 +3,14 @@ Example User Federation Provider
 
 This is an example of user federation backed by a simple properties file.  This properties file only contains username/password
 key pairs.  To deploy, build this directory then take the jar and copy it to the WEB-INF/lib of the keycloak server's
-WAR file.
+WAR file.  You will then have to restart the authentication server.
 
 The ClasspathPropertiesFederationProvider is an example of a readonly provider.  If you go to the Users/Federation
   page of the admin console you will see this provider listed under "classpath-properties.  To configure this provider you 
 specify a classpath to a properties file in the "path" field of the admin page for this plugin.  This example includes
 a "test-users.properties" within the JAR that you can use as the variable.
   
-The FilePropertiesFederationProvider is an exxample of a writable provider.  It synchronizes changes made to
+The FilePropertiesFederationProvider is an example of a writable provider.  It synchronizes changes made to
 username and password with the properties file.  If you go to the Users/Federation page of the admin console you will 
 see this provider listed under "file-properties".  To configure this provider you specify a fully qualified file path to 
 a properties file in the "path" field of the admin page for this plugin.  
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index 6c77ae7..5cc6390 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -347,7 +347,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
 
 });
 
-module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, application, applications,
+module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, application, applications, Notifications,
                                                           Application,
                                                           ApplicationRealmScopeMapping, ApplicationApplicationScopeMapping, ApplicationRole,
                                                           ApplicationAvailableRealmScopeMapping, ApplicationAvailableApplicationScopeMapping,
@@ -375,6 +375,7 @@ module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, 
             $scope.changed = false;
             application = angular.copy($scope.application);
             updateRealmRoles();
+            Notifications.success("Scope mappings updated.");
         });
     }
 
@@ -399,49 +400,40 @@ module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, 
         }
     }
 
-    $scope.addRealmRole = function() {
-        $http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm', $scope.selectedRealmRoles)
-            .success(updateRealmRoles);
-    };
-
-    $scope.deleteRealmRole = function() {
-        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/realm',
-            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}})
-            .success(updateRealmRoles);
-    };
-
-    $scope.addApplicationRole = function() {
-        $http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-                $scope.selectedApplicationRoles).success(updateAppRoles);
-    };
-
-    $scope.deleteApplicationRole = function() {
-        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
-    };
-
     $scope.changeApplication = function() {
         updateAppRoles();
     };
 
     $scope.addRealmRole = function() {
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
-                $scope.selectedRealmRoles).success(updateRealmRoles);
+                $scope.selectedRealmRoles).success(function() {
+                updateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+            });
     };
 
     $scope.deleteRealmRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/realm',
-            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(updateRealmRoles);
+            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function () {
+                updateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+            });
     };
 
     $scope.addApplicationRole = function() {
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-                $scope.selectedApplicationRoles).success(updateAppRoles);
+                $scope.selectedApplicationRoles).success(function () {
+                updateAppRoles();
+                Notifications.success("Scope mappings updated.");
+            });
     };
 
     $scope.deleteApplicationRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
+            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function () {
+                updateAppRoles();
+                Notifications.success("Scope mappings updated.");
+            });
     };
 
     updateRealmRoles();
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
index 3f6c154..b37d228 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
@@ -182,7 +182,7 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
 
 });
 
-module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications,
+module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications, Notifications,
                                                           OAuthClient,
                                                           OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole,
                                                           OAuthClientAvailableRealmScopeMapping, OAuthClientAvailableApplicationScopeMapping,
@@ -208,6 +208,7 @@ module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, 
         }, $scope.oauth, function() {
             $scope.changed = false;
             oauth = angular.copy($scope.oauth);
+            Notifications.success("Scope mappings updated.");
         });
 
     }
@@ -232,49 +233,43 @@ module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, 
         }
     }
 
-    $scope.addRealmRole = function() {
-        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm', $scope.selectedRealmRoles)
-            .success(updateRealmRoles);
-    };
-
-    $scope.deleteRealmRole = function() {
-        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/realm',
-            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}})
-            .success(updateRealmRoles);
-    };
-
-    $scope.addApplicationRole = function() {
-        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            $scope.selectedApplicationRoles).success(updateAppRoles);
-    };
-
-    $scope.deleteApplicationRole = function() {
-        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
-    };
-
     $scope.changeApplication = function() {
         updateAppRoles();
     };
 
     $scope.addRealmRole = function() {
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm',
-            $scope.selectedRealmRoles).success(updateRealmRoles);
+            $scope.selectedRealmRoles).success(function () {
+                updateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+            });
     };
 
     $scope.deleteRealmRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/realm',
-            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(updateRealmRoles);
+            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function () {
+                updateRealmRoles();
+                Notifications.success("Scope mappings updated.");
+
+            });
     };
 
     $scope.addApplicationRole = function() {
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            $scope.selectedApplicationRoles).success(updateAppRoles);
+            $scope.selectedApplicationRoles).success(function () {
+                updateAppRoles();
+                Notifications.success("Scope mappings updated.");
+
+            });
     };
 
     $scope.deleteApplicationRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
+            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function () {
+                updateAppRoles();
+                Notifications.success("Scope mappings updated.");
+
+            });
     };
 
     updateRealmRoles();
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index c4f7375..80634cf 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -1,4 +1,4 @@
-module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, applications, RealmRoleMapping,
+module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, applications, Notifications, RealmRoleMapping,
                                                   ApplicationRoleMapping, AvailableRealmRoleMapping, AvailableApplicationRoleMapping,
                                                   CompositeRealmRoleMapping, CompositeApplicationRoleMapping) {
     $scope.realm = realm;
@@ -34,6 +34,8 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
                     $scope.selectedApplicationRoles = [];
                     $scope.selectedApplicationMappings = [];
                 }
+                Notifications.success("Role mappings updated.");
+
             });
     };
 
@@ -53,6 +55,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
                     $scope.selectedApplicationRoles = [];
                     $scope.selectedApplicationMappings = [];
                 }
+                Notifications.success("Role mappings updated.");
             });
     };
 
@@ -64,6 +67,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
                 $scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.name});
                 $scope.selectedApplicationRoles = [];
                 $scope.selectedApplicationMappings = [];
+                Notifications.success("Role mappings updated.");
             });
     };
 
@@ -75,6 +79,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
                 $scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.name});
                 $scope.selectedApplicationRoles = [];
                 $scope.selectedApplicationMappings = [];
+                Notifications.success("Role mappings updated.");
             });
     };
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
index 35b2938..e5c9017 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-generic.html
@@ -4,9 +4,18 @@
         <li><a href="#/realms/{{realm.realm}}/users">User List</a></li>
         <li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
     </ul>
-    <h2></h2>
     <div id="content">
-        <h2 class="pull-left"><span>{{realm.realm}}</span> Provider Settings</h2>
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></li>
+            <li class="active">Provider Settings</li>
+        </ol>
+        <ol class="breadcrumb" data-ng-show="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li class="active">Add Federation Provider</li>
+        </ol>
+        <h2 class="pull-left" data-ng-hide="create">Provider Settings</h2>
+        <h2 class="pull-left" data-ng-show="create">Add Federation Provider</h2>
         <p class="subtitle"><span class="required">*</span> Required fields</p>
         <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
 
@@ -70,7 +79,7 @@
 
             <div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
                 <button kc-cancel data-ng-click="cancel()">Cancel</button>
-                <button kc-save data-ng-show="changed">Save</button>
+                <button kc-save>Save</button>
             </div>
 
             <div class="pull-right form-actions" data-ng-show="!create && access.manageUsers">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index feb53e4..3e9f3db 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -4,9 +4,18 @@
         <li><a href="#/realms/{{realm.realm}}/users">User List</a></li>
         <li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
     </ul>
-    <h2></h2>
     <div id="content">
-        <h2 class="pull-left"><span>{{realm.realm}}</span> Ldap Server Settings</h2>
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></li>
+            <li class="active">LDAP Settings</li>
+        </ol>
+        <ol class="breadcrumb" data-ng-show="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li class="active">Add LDAP Provider</li>
+        </ol>
+        <h2 class="pull-left" data-ng-hide="create">LDAP Provider Settings</h2>
+        <h2 class="pull-left" data-ng-show="create">Add LDAP Provider</h2>
         <p class="subtitle"><span class="required">*</span> Required fields</p>
         <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
             
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-list.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-list.html
index f8bf163..f6c52d8 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-list.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-list.html
@@ -5,10 +5,10 @@
 </div>
 <div id="content-area" class="col-md-9" role="main">
     <ul class="nav nav-tabs nav-tabs-pf">
-        <li class="active"><a href="">Choose realm to manage</a></li>
+        <li class="active"><a href="">Realm List</a></li>
     </ul>
     <div id="content">
-        <h2 class="margin-top">Realms</h2>
+        <h2 class="margin-top">Choose Realm to Manage</h2>
         <table class="table table-striped table-bordered">
             <thead>
             <tr>
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index f03d410..acee11d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -192,6 +192,7 @@ public class UserFederationResource {
     @GET
     @Path("instances")
     @Produces("application/json")
+    @NoCache
     public List<UserFederationProviderRepresentation> getUserFederationInstances() {
         logger.info("getUserFederationInstances");
         auth.requireManage();
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index c9355c8..9e5d039 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -229,7 +229,7 @@ public class TokenService {
         }
 
         if (!realm.isPasswordCredentialGrantAllowed()) {
-            return createError("not_enabled", "Resource Owner Password Credentials Grant not enabled", Response.Status.FORBIDDEN);
+            return createError("not_enabled", "Direct Grant REST API not enabled", Response.Status.FORBIDDEN);
         }
 
         audit.event(EventType.LOGIN).detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");