[KEYCLOAK-3152] - Keycloak Authorization JS Adapter
This commit is contained in:
parent
8402cedd82
commit
905421a292
3 changed files with 251 additions and 27 deletions
|
@ -58,6 +58,25 @@
|
||||||
<goal>minify</goal>
|
<goal>minify</goal>
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</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>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|
170
adapters/oidc/js/src/main/resources/keycloak-authz.js
Normal file
170
adapters/oidc/js/src/main/resources/keycloak-authz.js
Normal file
|
@ -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 );
|
|
@ -44,35 +44,82 @@ public class JsResource {
|
||||||
@GET
|
@GET
|
||||||
@Path("/keycloak.js")
|
@Path("/keycloak.js")
|
||||||
@Produces("text/javascript")
|
@Produces("text/javascript")
|
||||||
public Response getJs() {
|
public Response getKeycloakJs() {
|
||||||
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.js");
|
return getJs("keycloak.js");
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/{version}/keycloak.js")
|
@Path("/{version}/keycloak.js")
|
||||||
@Produces("text/javascript")
|
@Produces("text/javascript")
|
||||||
public Response getJsWithVersion(@PathParam("version") String version) {
|
public Response getKeycloakJsWithVersion(@PathParam("version") String version) {
|
||||||
if (!version.equals(Version.RESOURCES_VERSION)) {
|
if (!version.equals(Version.RESOURCES_VERSION)) {
|
||||||
return Response.status(Response.Status.NOT_FOUND).build();
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getJs();
|
return getKeycloakJs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/keycloak.min.js")
|
@Path("/keycloak.min.js")
|
||||||
@Produces("text/javascript")
|
@Produces("text/javascript")
|
||||||
public Response getMinJs() {
|
public Response getKeycloakMinJs() {
|
||||||
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.min.js");
|
return getJs("keycloak.min.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{version}/keycloak.min.js")
|
||||||
|
@Produces("text/javascript")
|
||||||
|
public Response getKeycloakMinJsWithVersion(@PathParam("version") String version) {
|
||||||
|
if (!version.equals(Version.RESOURCES_VERSION)) {
|
||||||
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getKeycloakMinJs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get keycloak-authz.js file for javascript clients
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/keycloak-authz.js")
|
||||||
|
@Produces("text/javascript")
|
||||||
|
public Response getKeycloakAuthzJs() {
|
||||||
|
return getJs("keycloak-authz.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
@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("/keycloak-authz.min.js")
|
||||||
|
@Produces("text/javascript")
|
||||||
|
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 getKeycloakAuthzMinJs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response getJs(String name) {
|
||||||
|
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name);
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
CacheControl cacheControl = new CacheControl();
|
CacheControl cacheControl = new CacheControl();
|
||||||
cacheControl.setNoTransform(false);
|
cacheControl.setNoTransform(false);
|
||||||
|
@ -83,16 +130,4 @@ public class JsResource {
|
||||||
return Response.status(Response.Status.NOT_FOUND).build();
|
return Response.status(Response.Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{version}/keycloak.min.js")
|
|
||||||
@Produces("text/javascript")
|
|
||||||
public Response getMinJsWithVersion(@PathParam("version") String version) {
|
|
||||||
if (!version.equals(Version.RESOURCES_VERSION)) {
|
|
||||||
return Response.status(Response.Status.NOT_FOUND).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMinJs();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue