keycloak-uncached

Details

diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml
index 315f4b7..b03ccea 100755
--- a/adapters/oidc/js/pom.xml
+++ b/adapters/oidc/js/pom.xml
@@ -58,6 +58,25 @@
                             <goal>minify</goal>
                         </goals>
                     </execution>
+                    <execution>
+                        <id>min-authz-js</id>
+                        <phase>compile</phase>
+                        <configuration>
+                            <charset>utf-8</charset>
+                            <webappSourceDir>${basedir}/src/main/resources</webappSourceDir>
+                            <jsSourceDir>.</jsSourceDir>
+                            <jsSourceFiles>
+                                <jsSourceFile>keycloak-authz.js</jsSourceFile>
+                            </jsSourceFiles>
+
+                            <webappTargetDir>${project.build.directory}/classes</webappTargetDir>
+                            <jsTargetDir>.</jsTargetDir>
+                            <jsFinalFile>keycloak-authz.js</jsFinalFile>
+                        </configuration>
+                        <goals>
+                            <goal>minify</goal>
+                        </goals>
+                    </execution>
                 </executions>
             </plugin>
 		</plugins>
diff --git a/adapters/oidc/js/src/main/resources/keycloak-authz.js b/adapters/oidc/js/src/main/resources/keycloak-authz.js
new file mode 100644
index 0000000..7658352
--- /dev/null
+++ b/adapters/oidc/js/src/main/resources/keycloak-authz.js
@@ -0,0 +1,170 @@
+/*
+ *  Copyright 2016 Red Hat, Inc. and/or its affiliates
+ *  and other contributors as indicated by the @author tags.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+(function( window, undefined ) {
+    
+    var KeycloakAuthorization = function (keycloak) {
+        var _instance = this;
+        this.rpt = null;
+
+        this.init = function () {
+            var request = new XMLHttpRequest();
+
+            request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
+            request.onreadystatechange = function () {
+                if (request.readyState == 4) {
+                    if (request.status == 200) {
+                        _instance.config = JSON.parse(request.responseText);
+                    } else {
+                        console.error('Could not obtain configuration from server.');
+                    }
+                }
+            }
+
+            request.send(null);
+        };
+
+        /**
+         * This method enables client applications to better integrate with resource servers protected by a Keycloak
+         * policy enforcer.
+         *
+         * In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
+         * necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
+         * depending on how the policy enforcer at the resource server was configured.
+         */
+        this.authorize = function (wwwAuthenticateHeader) {
+            this.then = function (onGrant, onDeny, onError) {
+                if (wwwAuthenticateHeader.startsWith('UMA')) {
+                    var params = wwwAuthenticateHeader.split(',');
+
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'ticket') {
+                            var request = new XMLHttpRequest();
+
+                            request.open('POST', _instance.config.rpt_endpoint, true);
+                            request.setRequestHeader('Content-Type', 'application/json')
+                            request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
+
+                            request.onreadystatechange = function () {
+                                if (request.readyState == 4) {
+                                    var status = request.status;
+
+                                    if (status >= 200 && status < 300) {
+                                        var rpt = JSON.parse(request.responseText).rpt;
+                                        _instance.rpt = rpt;
+                                        onGrant(rpt);
+                                    } else if (status == 403) {
+                                        if (onDeny) {
+                                            onDeny();
+                                        } else {
+                                            console.error('Authorization request was denied by the server.');
+                                        }
+                                    } else {
+                                        if (onError) {
+                                            onError();
+                                        } else {
+                                            console.error('Could not obtain authorization data from server.');
+                                        }
+                                    }
+                                }
+                            };
+
+                            var ticket = param[1].substring(1, param[1].length - 1).trim();
+
+                            request.send(JSON.stringify(
+                                {
+                                    ticket: ticket,
+                                    rpt: _instance.rpt
+                                }
+                            ));
+                        }
+                    }
+                } else if (wwwAuthenticateHeader.startsWith('KC_ETT')) {
+                    var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
+                    var clientId = null;
+
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'realm') {
+                            clientId = param[1].substring(1, param[1].length - 1).trim();
+                        }
+                    }
+
+                    _instance.entitlement(clientId).then(onGrant, onDeny, onError);
+                }
+            };
+
+            /**
+             * Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
+             */
+            this.entitlement = function (resourceSeververId) {
+                this.then = function (onGrant, onDeny, onError) {
+                    var request = new XMLHttpRequest();
+
+                    request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
+                    request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
+
+                    request.onreadystatechange = function () {
+                        if (request.readyState == 4) {
+                            var status = request.status;
+
+                            if (status >= 200 && status < 300) {
+                                var rpt = JSON.parse(request.responseText).rpt;
+                                _instance.rpt = rpt;
+                                onGrant(rpt);
+                            } else if (status == 403) {
+                                if (onDeny) {
+                                    onDeny();
+                                } else {
+                                    console.error('Authorization request was denied by the server.');
+                                }
+                            } else {
+                                if (onError) {
+                                    onError();
+                                } else {
+                                    console.error('Could not obtain authorization data from server.');
+                                }
+                            }
+                        }
+                    };
+
+                    request.send(null);
+                };
+
+                return this;
+            };
+
+            return this;
+        };
+
+        this.init(this);
+    };
+
+    if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+        module.exports = KeycloakAuthorization;
+    } else {
+        window.KeycloakAuthorization = KeycloakAuthorization;
+
+        if ( typeof define === "function" && define.amd ) {
+            define( "keycloak-authorization", [], function () { return KeycloakAuthorization; } );
+        }
+    }
+})( window );
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/JsResource.java b/services/src/main/java/org/keycloak/services/resources/JsResource.java
index 32161a4..c74abf0 100755
--- a/services/src/main/java/org/keycloak/services/resources/JsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/JsResource.java
@@ -44,55 +44,90 @@ public class JsResource {
     @GET
     @Path("/keycloak.js")
     @Produces("text/javascript")
-    public Response getJs() {
-        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.js");
-        if (inputStream != null) {
-            CacheControl cacheControl = new CacheControl();
-            cacheControl.setNoTransform(false);
-            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+    public Response getKeycloakJs() {
+        return getJs("keycloak.js");
+    }
 
-            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
-        } else {
+    @GET
+    @Path("/{version}/keycloak.js")
+    @Produces("text/javascript")
+    public Response getKeycloakJsWithVersion(@PathParam("version") String version) {
+        if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
+
+        return getKeycloakJs();
     }
 
     @GET
-    @Path("/{version}/keycloak.js")
+    @Path("/keycloak.min.js")
+    @Produces("text/javascript")
+    public Response getKeycloakMinJs() {
+        return getJs("keycloak.min.js");
+    }
+
+    @GET
+    @Path("/{version}/keycloak.min.js")
     @Produces("text/javascript")
-    public Response getJsWithVersion(@PathParam("version") String version) {
+    public Response getKeycloakMinJsWithVersion(@PathParam("version") String version) {
         if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
 
-        return getJs();
+        return getKeycloakMinJs();
     }
 
+    /**
+     * Get keycloak-authz.js file for javascript clients
+     *
+     * @return
+     */
     @GET
-    @Path("/keycloak.min.js")
+    @Path("/keycloak-authz.js")
     @Produces("text/javascript")
-    public Response getMinJs() {
-        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.min.js");
-        if (inputStream != null) {
-            CacheControl cacheControl = new CacheControl();
-            cacheControl.setNoTransform(false);
-            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+    public Response getKeycloakAuthzJs() {
+        return getJs("keycloak-authz.js");
+    }
 
-            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
-        } else {
+    @GET
+    @Path("/{version}/keycloak-authz.js")
+    @Produces("text/javascript")
+    public Response getKeycloakAuthzJsWithVersion(@PathParam("version") String version) {
+        if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
+
+        return getKeycloakAuthzJs();
     }
 
     @GET
-    @Path("/{version}/keycloak.min.js")
+    @Path("/keycloak-authz.min.js")
     @Produces("text/javascript")
-    public Response getMinJsWithVersion(@PathParam("version") String version) {
+    public Response getKeycloakAuthzMinJs() {
+        return getJs("keycloak-authz.min.js");
+    }
+
+    @GET
+    @Path("/{version}/keycloak-authz.min.js")
+    @Produces("text/javascript")
+    public Response getKeycloakAuthzMinJsWithVersion(@PathParam("version") String version) {
         if (!version.equals(Version.RESOURCES_VERSION)) {
             return Response.status(Response.Status.NOT_FOUND).build();
         }
 
-        return getMinJs();
+        return getKeycloakAuthzMinJs();
     }
 
+    private Response getJs(String name) {
+        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name);
+        if (inputStream != null) {
+            CacheControl cacheControl = new CacheControl();
+            cacheControl.setNoTransform(false);
+            cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
+
+            return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build();
+        } else {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+    }
 }