Skip to content

Commit

Permalink
[KEYCLOAK-3152] - Keycloak Authorization JS Adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro Igor committed Jun 22, 2016
1 parent 8402ced commit 905421a
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 23 deletions.
19 changes: 19 additions & 0 deletions adapters/oidc/js/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
170 changes: 170 additions & 0 deletions adapters/oidc/js/src/main/resources/keycloak-authz.js
Original file line number Diff line number Diff line change
@@ -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 );
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}

0 comments on commit 905421a

Please sign in to comment.