diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java index add1c83927..3ae286fc21 100644 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java @@ -129,7 +129,7 @@ public abstract class AbstractPolicyEnforcer { Set allowedScopes = permission.getScopes(); if (permission.getResourceSetId() != null) { - if (permission.getResourceSetId().equals(actualPathConfig.getId())) { + if (isResourcePermission(actualPathConfig, permission)) { if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) { LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions); if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) { @@ -211,6 +211,7 @@ public abstract class AbstractPolicyEnforcer { config.setScopes(originalConfig.getScopes()); config.setMethods(originalConfig.getMethods()); config.setInstance(true); + config.setParentConfig(originalConfig); this.paths.add(config); @@ -240,4 +241,16 @@ public abstract class AbstractPolicyEnforcer { private AuthorizationContext createAuthorizationContext(AccessToken accessToken) { return new AuthorizationContext(accessToken, this.paths); } + + private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) { + // first we try a match using resource id + boolean resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getId()); + + // as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission + if (!resourceMatch && actualPathConfig.isInstance()) { + resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getParentConfig().getId()); + } + + return resourceMatch; + } } diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml index 315f4b7fa0..b03ccea898 100755 --- a/adapters/oidc/js/pom.xml +++ b/adapters/oidc/js/pom.xml @@ -58,6 +58,25 @@ minify + + min-authz-js + compile + + utf-8 + ${basedir}/src/main/resources + . + + keycloak-authz.js + + + ${project.build.directory}/classes + . + keycloak-authz.js + + + minify + + 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 0000000000..7658352ade --- /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/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java index a693263749..be83987750 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java @@ -89,7 +89,7 @@ public class HttpMethod { int statusCode = statusLine.getStatusCode(); if (statusCode < 200 || statusCode >= 300) { - throw new HttpResponseException(statusCode, statusLine.getReasonPhrase(), bytes); + throw new HttpResponseException("Unexpected response from server: " + statusCode + " / " + statusLine.getReasonPhrase(), statusCode, statusLine.getReasonPhrase(), bytes); } if (bytes == null) { diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java index 9b783e77ae..3531f40a5f 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpResponseException.java @@ -26,7 +26,8 @@ public class HttpResponseException extends RuntimeException { private final String reasonPhrase; private final byte[] bytes; - public HttpResponseException(int statusCode, String reasonPhrase, byte[] bytes) { + public HttpResponseException(String message, int statusCode, String reasonPhrase, byte[] bytes) { + super(message); this.statusCode = statusCode; this.reasonPhrase = reasonPhrase; this.bytes = bytes; diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java index 98a4045011..5145ec773d 100644 --- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java @@ -17,6 +17,7 @@ */ package org.keycloak.representations.adapters.config; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; @@ -99,6 +100,9 @@ public class PolicyEnforcerConfig { private String id; private boolean instance; + @JsonIgnore + private PathConfig parentConfig; + public String getPath() { return this.path; } @@ -169,6 +173,14 @@ public class PolicyEnforcerConfig { public void setInstance(boolean instance) { this.instance = instance; } + + public void setParentConfig(PathConfig parentConfig) { + this.parentConfig = parentConfig; + } + + public PathConfig getParentConfig() { + return parentConfig; + } } public static class MethodConfig { diff --git a/examples/authz/hello-world-authz-service/README.md b/examples/authz/hello-world-authz-service/README.md new file mode 100644 index 0000000000..a0cc40fae3 --- /dev/null +++ b/examples/authz/hello-world-authz-service/README.md @@ -0,0 +1,47 @@ +# About the Example Application + +This is a simple application to get you started with Keycloak Authorization Services. + +It provides a single page application which is protected by a policy enforcer that decides whether an user can access +that page or not based on the permissions obtained from a Keycloak Server. + +## Create the Example Realm and a Resource Server + +Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console. + +Now, create a new realm based on the following configuration file: + + examples/authz/hello-world-authz-service/hello-world-authz-realm.json + +That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm +into Keycloak, check the Keycloak's reference documentation. + +After importing that file, you'll have a new realm called ``hello-world-authz``. + +Now, let's import another configuration using the Administration Console in order to configure the client application ``hello-world-authz-service`` as a resource server with all resources, scopes, permissions and policies. + +Click on ``Clients`` on the left side menu. Click on the ``hello-world-authz-service`` on the client listing page. This will +open the ``Client Details`` page. Once there, click on the `Authorization` tab. + +Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at: + + examples/authz/hello-world-authz-service/hello-world-authz-service.json + +Now click ``Upload`` and the resource server will be updated accordingly. + +## Deploy and Run the Example Application + +To deploy the example application, follow these steps: + + cd examples/authz/hello-world-authz-service + mvn clean package wildfly:deploy + +Now, try to access the client application using the following URL: + + http://localhost:8080/hello-world-authz-service + +If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials: + +* username: jdoe / password: jdoe +* username: alice / password: alice + diff --git a/examples/authz/hello-world-authz-service/hello-world-authz-realm.json b/examples/authz/hello-world-authz-service/hello-world-authz-realm.json new file mode 100644 index 0000000000..3ab917c058 --- /dev/null +++ b/examples/authz/hello-world-authz-service/hello-world-authz-realm.json @@ -0,0 +1,49 @@ +{ + "realm" : "hello-world-authz", + "enabled" : true, + "privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=", + "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB", + "certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91", + "requiredCredentials" : [ "password" ], + "users" : + [ + { + "username" : "alice", + "enabled" : true, + "credentials" : [ { + "type" : "password", + "value" : "alice" + } ], + "realmRoles" : ["uma_authorization"] + }, + { + "username" : "jdoe", + "enabled" : true, + "credentials" : [ { + "type" : "password", + "value" : "jdoe" + } ], + "realmRoles" : ["uma_authorization"] + }, + { + "username" : "service-account-hello-world-authz-service", + "enabled" : true, + "serviceAccountClientId" : "hello-world-authz-service", + "clientRoles": { + "hello-world-authz-service" : ["uma_protection"] + } + } + ], + "clients" : [ + { + "clientId" : "hello-world-authz-service", + "secret" : "secret", + "authorizationServicesEnabled" : true, + "enabled" : true, + "redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ], + "baseUrl": "http://localhost:8080/hello-world-authz-service", + "adminUrl": "http://localhost:8080/hello-world-authz-service", + "directAccessGrantsEnabled" : true + } + ] +} \ No newline at end of file diff --git a/examples/authz/hello-world-authz-service/hello-world-authz-service.json b/examples/authz/hello-world-authz-service/hello-world-authz-service.json new file mode 100644 index 0000000000..ea56e6268d --- /dev/null +++ b/examples/authz/hello-world-authz-service/hello-world-authz-service.json @@ -0,0 +1,30 @@ +{ + "resources": [ + { + "name": "Default Resource", + "uri": "/*", + "type": "urn:hello-world-authz-service:resources:default" + } + ], + "policies": [ + { + "name": "Only From Realm Policy", + "description": "A policy that grants access only for users within this realm", + "type": "js", + "config": { + "applyPolicies": "[]", + "code": "var context = $evaluation.getContext();\n\n// using attributes from the evaluation context to obtain the realm\nvar contextAttributes = context.getAttributes();\nvar realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n\n// using attributes from the identity to obtain the issuer\nvar identity = context.getIdentity();\nvar identityAttributes = identity.getAttributes();\nvar issuer = identityAttributes.getValue('iss').asString(0);\n\n// only users from the realm have access granted \nif (issuer.endsWith(realmName)) {\n $evaluation.grant();\n}" + } + }, + { + "name": "Default Permission", + "description": "A permission that applies to the default resource type", + "type": "resource", + "config": { + "defaultResourceType": "urn:hello-world-authz-service:resources:default", + "default": "true", + "applyPolicies": "[\"Only From Realm Policy\"]" + } + } + ] +} \ No newline at end of file diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json index 04c048680e..f303fe1e55 100644 --- a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json @@ -1,11 +1,11 @@ { "realm": "hello-world-authz", - "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmm2Nso+rUOYUYc4hO67LSf4s0pAKcqUbWWycS3fcz6Q4jg/SsBbIBJJXOMVR9GqwyTCVTH5s8Rb0+0pA+UrbZfMG2XIDnJoaGfJj9DvJwQkD+vzTvaS5q0ilP0tPlbusI5pyMi9xx+cjJBOvKR2GxjhcKrgb21lpmGcA1F1CPO3y/DT8GzTKg+9/nPKt1dKEUD7P5Uy5N7d8zz1fuOSLb5G267T1fKJvi6am8kCgM+agFVQ23j7w/aJ7T1EHUCZdaJ+aSODSYl8dM4RFNTjda0KMHHXqMMvd2+g8lZ0lAfstHywqZtCcHc9ULClVvQmQyXovn2qTktHAcD6BHTAgQIDAQAB", + "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required": "external", "resource": "hello-world-authz-service", "credentials": { - "secret": "a7672d93-ea27-44a3-baa6-ba3536609067" + "secret": "secret" }, "policy-enforcer": { "on-deny-redirect-to" : "/hello-world-authz-service/error.jsp" diff --git a/examples/authz/hello-world/hello-world-authz-realm.json b/examples/authz/hello-world/hello-world-authz-realm.json index a263c6983f..3ab917c058 100644 --- a/examples/authz/hello-world/hello-world-authz-realm.json +++ b/examples/authz/hello-world/hello-world-authz-realm.json @@ -12,16 +12,18 @@ "enabled" : true, "credentials" : [ { "type" : "password", - "value" : "password" - } ] + "value" : "alice" + } ], + "realmRoles" : ["uma_authorization"] }, { "username" : "jdoe", "enabled" : true, "credentials" : [ { "type" : "password", - "value" : "password" - } ] + "value" : "jdoe" + } ], + "realmRoles" : ["uma_authorization"] }, { "username" : "service-account-hello-world-authz-service", @@ -38,7 +40,9 @@ "secret" : "secret", "authorizationServicesEnabled" : true, "enabled" : true, - "redirectUris" : [ "http://localhost:8080/hello-world-authz-service" ], + "redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ], + "baseUrl": "http://localhost:8080/hello-world-authz-service", + "adminUrl": "http://localhost:8080/hello-world-authz-service", "directAccessGrantsEnabled" : true } ] diff --git a/examples/authz/hello-world/hello-world-authz-service.json b/examples/authz/hello-world/hello-world-authz-service.json index 24bd27f60b..ea56e6268d 100644 --- a/examples/authz/hello-world/hello-world-authz-service.json +++ b/examples/authz/hello-world/hello-world-authz-service.json @@ -1,24 +1,29 @@ { "resources": [ { - "name": "Hello World Resource" + "name": "Default Resource", + "uri": "/*", + "type": "urn:hello-world-authz-service:resources:default" } ], "policies": [ { - "name": "Only Special Users Policy", - "type": "user", - "logic": "POSITIVE", + "name": "Only From Realm Policy", + "description": "A policy that grants access only for users within this realm", + "type": "js", "config": { - "users": "[\"alice\"]" + "applyPolicies": "[]", + "code": "var context = $evaluation.getContext();\n\n// using attributes from the evaluation context to obtain the realm\nvar contextAttributes = context.getAttributes();\nvar realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n\n// using attributes from the identity to obtain the issuer\nvar identity = context.getIdentity();\nvar identityAttributes = identity.getAttributes();\nvar issuer = identityAttributes.getValue('iss').asString(0);\n\n// only users from the realm have access granted \nif (issuer.endsWith(realmName)) {\n $evaluation.grant();\n}" } }, { - "name": "Hello World Resource Permission", + "name": "Default Permission", + "description": "A permission that applies to the default resource type", "type": "resource", "config": { - "resources": "[\"Hello World Resource\"]", - "applyPolicies": "[\"Only Special Users Policy\"]" + "defaultResourceType": "urn:hello-world-authz-service:resources:default", + "default": "true", + "applyPolicies": "[\"Only From Realm Policy\"]" } } ] diff --git a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java index 75ee0d3de3..2ab8788694 100644 --- a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java +++ b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java @@ -49,7 +49,7 @@ public class AuthorizationClientExample { // query the server for a resource with a given name Set resourceId = authzClient.protection() .resource() - .findByFilter("name=Hello World Resource"); + .findByFilter("name=Default Resource"); // obtian a Entitlement API Token in order to get access to the Entitlement API. // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement @@ -119,7 +119,7 @@ public class AuthorizationClientExample { EntitlementRequest request = new EntitlementRequest(); PermissionRequest permission = new PermissionRequest(); - permission.setResourceSetName("Hello World Resource"); + permission.setResourceSetName("Default Resource"); request.addPermission(permission); @@ -157,6 +157,6 @@ public class AuthorizationClientExample { * @return a string representing a EAT */ private static String getEntitlementAPIToken(AuthzClient authzClient) { - return authzClient.obtainAccessToken("alice", "password").getToken(); + return authzClient.obtainAccessToken("alice", "alice").getToken(); } } diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/js/app.js b/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/js/app.js deleted file mode 100755 index 8913ead302..0000000000 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/js/app.js +++ /dev/null @@ -1,207 +0,0 @@ -var module = angular.module('photoz-uma', ['ngRoute', 'ngResource']); - -var Identity = {}; - -angular.element(document).ready(function ($http) { - var keycloakAuth = new Keycloak('keycloak.json'); - Identity.loggedIn = false; - keycloakAuth.init({onLoad: 'login-required'}).success(function () { - Identity.loggedIn = true; - Identity.authz = keycloakAuth; - Identity.logout = function () { - Identity.loggedIn = false; - Identity.claim = {}; - Identity.authc = null; - window.location = this.authz.authServerUrl + "/realms/photoz-uma/protocol/openid-connect/logout?redirect_uri=http://localhost:8080/photoz-uma-html5-client/index.html"; - Identity.authz = null; - }; - Identity.claim = {}; - Identity.claim.name = Identity.authz.idTokenParsed.name; - Identity.hasRole = function (name) { - if (Identity.authz && Identity.authz.hasRealmRole(name)) { - return true; - } - return false; - }; - Identity.isAdmin = function () { - return this.hasRole("admin"); - }; - Identity.authc = {}; - Identity.authc.token = Identity.authz.token; - module.factory('Identity', function () { - return Identity; - }); - angular.bootstrap(document, ["photoz-uma"]); - }).error(function () { - window.location.reload(); - }); -}); -module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Album, Identity) { - Album.query(function (albums) { - $scope.albums = albums; - }); - - $scope.Identity = Identity; - - $scope.deleteAlbum = function (album) { - new Album(album).$delete({id: album.id}, function () { - $route.reload(); - }); - } -}); -module.controller('TokenCtrl', function ($scope, Identity) { - $scope.showRpt = function () { - document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.uma.rpt.rpt), null, ' '); - } - - $scope.showAccessToken = function () { - document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.authc.token), null, ' '); - } - - $scope.requestEntitlements = function () { - var request = new XMLHttpRequest(); - - request.open("GET", "http://localhost:8080/auth/realms/photoz-uma/authz/entitlement/photoz-uma-restful-api", true); - request.setRequestHeader("Authorization", "Bearer " + Identity.authc.token); - request.onreadystatechange = function () { - if (request.readyState == 4 && request.status == 200) { - Identity.uma.rpt = JSON.parse(request.responseText); - } - } - - request.send(null); - } -}); -module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) { - $scope.album = {}; - if ($routeParams.id) { - $scope.album = Album.get({id: $routeParams.id}); - } - $scope.create = function () { - var newAlbum = new Album($scope.album); - newAlbum.$save({}, function (data) { - $location.path('/'); - }); - }; -}); -module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $location, Profile) { - $scope.profile = Profile.get(); -}); -module.controller('AdminAlbumCtrl', function ($scope, $http, $route, AdminAlbum, Album) { - $scope.albums = {}; - $http.get('/photoz-uma-restful-api/admin/album').success(function (data) { - $scope.albums = data; - }); - $scope.deleteAlbum = function (album) { - var newAlbum = new Album(album); - newAlbum.$delete({id: album.id}, function () { - $route.reload(); - }); - } -}); -module.factory('Album', ['$resource', function ($resource) { - return $resource('http://localhost:8080/photoz-uma-restful-api/album/:id'); -}]); -module.factory('Profile', ['$resource', function ($resource) { - return $resource('http://localhost:8080/photoz-uma-restful-api/profile'); -}]); -module.factory('AdminAlbum', ['$resource', function ($resource) { - return $resource('http://localhost:8080/photoz-uma-restful-api/admin/album/:id'); -}]); -module.factory('authInterceptor', function ($q, $injector, $timeout, Identity) { - return { - request: function (request) { - document.getElementById("output").innerHTML = ''; - if (Identity.uma && Identity.uma.rpt && request.url.indexOf('/authorize') == -1) { - retries = 0; - request.headers.Authorization = 'Bearer ' + Identity.uma.rpt.rpt; - } else { - request.headers.Authorization = 'Bearer ' + Identity.authc.token; - } - return request; - }, - responseError: function (rejection) { - if (rejection.status == 403 || rejection.status == 401) { - var retry = (!rejection.config.retry || rejection.config.retry < 1); - - if (!retry) { - document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.'; - return $q.reject(rejection); - } - - if (rejection.config.url.indexOf('/authorize') == -1 && retry) { - if (rejection.status == 401) { - console.log("Here"); - var authenticateHeader = rejection.headers('WWW-Authenticate'); - - if (authenticateHeader.startsWith('UMA')) { - var params = authenticateHeader.split(','); - - for (i = 0; i < params.length; i++) { - var param = params[i].split('='); - - if (param[0] == 'ticket') { - var ticket = param[1].substring(1, param[1].length - 1).trim(); - - var data = JSON.stringify({ - ticket: ticket, - rpt: Identity.uma ? Identity.uma.rpt.rpt : "" - }); - - var $http = $injector.get("$http"); - - var deferred = $q.defer(); - - $http.post('http://localhost:8080/auth/realms/photoz-uma/authz/authorize', data, {headers: {"Authorization": "Bearer " + Identity.authc.token}}) - .then(function (authzResponse) { - if (authzResponse.data) { - Identity.uma = {}; - Identity.uma.rpt = authzResponse.data; - } - deferred.resolve(rejection); - }, function (authzResponse) { - document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.'; - }); - - var promise = deferred.promise; - - return promise.then(function (res) { - if (!res.config.retry) { - res.config.retry = 1; - } else { - res.config.retry++; - } - return $http(res.config).then(function (response) { - return response; - }); - }); - } - } - } - } - } - } - - return $q.reject(rejection); - } - }; -}); -module.config(function ($httpProvider, $routeProvider) { - $httpProvider.interceptors.push('authInterceptor'); - $routeProvider.when('/', { - templateUrl: 'partials/home.html', - controller: 'GlobalCtrl' - }).when('/album/create', { - templateUrl: 'partials/album/create.html', - controller: 'AlbumCtrl', - }).when('/album/:id', { - templateUrl: 'partials/album/detail.html', - controller: 'AlbumCtrl', - }).when('/admin/album', { - templateUrl: 'partials/admin/albums.html', - controller: 'AdminAlbumCtrl', - }).when('/profile', { - templateUrl: 'partials/profile.html', - controller: 'ProfileCtrl', - }); -}); \ No newline at end of file diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/index.html b/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/index.html deleted file mode 100644 index 8318c86b35..0000000000 --- a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/index.html +++ /dev/null @@ -1 +0,0 @@ -Test \ No newline at end of file diff --git a/examples/authz/photoz-uma/README.md b/examples/authz/photoz/README.md similarity index 63% rename from examples/authz/photoz-uma/README.md rename to examples/authz/photoz/README.md index 61243eeee0..c915a1ffc9 100644 --- a/examples/authz/photoz-uma/README.md +++ b/examples/authz/photoz/README.md @@ -4,17 +4,16 @@ This is a simple application based on HTML5+AngularJS+JAX-RS that will introduce Basically, it is a project containing three modules: -* **photoz-uma-restful-api**, with a simple RESTFul API based on JAX-RS and acting as a regular **client application**. -* **photoz-uma-html5-client**, with a HTML5+AngularJS client that will consume the RESTful API and acting as a **resource server**. -* **photoz-uma-authz-policy**, with a simple project with some rule-based policies using JBoss Drools. +* **photoz-restful-api**, a simple RESTFul API based on JAX-RS and acting as a resource server. +* **photoz-html5-client**, a HTML5+AngularJS client that will consume the RESTful API published by a resource resourcer. +* **photoz-authz-policy**, a simple project with some rule-based policies using JBoss Drools. For this application, users can be regular users or administrators. Regular users can create/view/delete their albums -and administrators can view the albums for all users. +and administrators can do anything. -In Keycloak, albums are resources that must be protected based on a set of policies that defines who and how can access them. -Beside that, resources belong to a specific resource server, in this case to the *photoz-uma-restful-api*. +In Keycloak, albums are resources that must be protected based on a set of policies that defines who and how can access them. -The resources are also associated with a set of scopes that define a specific access context. In this case, albums have three main scopes: +The resources are also associated with a set of scopes that defines a specific access context. In this case, albums have three main scopes: * urn:photoz.com:scopes:album:create * urn:photoz.com:scopes:album:view @@ -26,12 +25,14 @@ The authorization requirements for this example application are based on the fol * For instance, Alice can create, view and delete her albums. -* Only the owner and administrators can delete albums. Here we are considering policies based on the *urn:photoz.com:scopes:album:delete* +* Only the owner and administrators can delete albums. Here we are considering policies based on the *urn:photoz.com:scopes:album:delete* scope * For instance, only Alice can delete her album. * Only administrators can access the Administration API (which basically provides ways to query albums for all users) +* Administrators are only authorized to access resources if the client's ip address is well known + That said, this application will show you how to use the Keycloak to define policies using: * Role-based Access Control @@ -46,11 +47,11 @@ It also provides some background on how you can actually protect your JAX-RS end ## Create the Example Realm and a Resource Server -Considering that your AuthZ Server is up and running, log in to the Keycloak Administration Console. +Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console. Now, create a new realm based on the following configuration file: - examples/authz/photoz/photoz-uma-realm.json + examples/authz/photoz/photoz-realm.json That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm into Keycloak, check the Keycloak's reference documentation. @@ -58,42 +59,40 @@ into Keycloak, check the Keycloak's reference documentation. After importing that file, you'll have a new realm called ``photoz``. Back to the command-line, build the example application. This step is necessary given that we're using policies based on -JBoss Drools, which require ``photoz-uma-authz-policy`` artifact installed into your local maven repository. +JBoss Drools, which require ``photoz-authz-policy`` artifact installed into your local maven repository. cd examples/authz/photoz mvn clean install -Now, let's import another configuration using the Administration Console in order to configure the ``photoz-uma-restful-api`` as a resource server with all resources, scopes, permissions and policies. +Now, let's import another configuration using the Administration Console in order to configure the client application ``photoz-restful-api`` as a resource server with all resources, scopes, permissions and policies. -Click on ``Authorization`` on the left side menu. Click on the ``Create`` button on the top of the resource server table. This will -open the page that allows you to create a new resource server. +Click on ``Clients`` on the left side menu. Click on the ``photoz-restful-api`` on the client listing page. This will +open the ``Client Details`` page. Once there, click on the `Authorization` tab. Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at: - examples/authz/photoz/photoz-uma-restful-api/photoz-uma-restful-api-authz-config.json + examples/authz/photoz/photoz-restful-api/photoz-restful-api-authz-config.json -Now click ``Upload`` and a new resource server will be created based on the ``photoz-uma-restful-api`` client application. +Now click ``Upload`` and the resource server will be updated accordingly. ## Deploy and Run the Example Applications To deploy the example applications, follow these steps: - cd examples/authz/photoz/photoz-uma-html5-client - mvn wildfly:deploy + cd examples/authz/photoz/photoz-html5-client + mvn clean package wildfly:deploy And then: - cd examples/authz/photoz/photoz-uma-restful-api - mvn wildfly:deploy + cd examples/authz/photoz/photoz-restful-api + mvn clean package wildfly:deploy Now, try to access the client application using the following URL: - http://localhost:8080/photoz-uma-html5-client + http://localhost:8080/photoz-html5-client If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials: * username: jdoe / password: jdoe * username: alice / password: alice -* username: admin / password: admin - - +* username: admin / password: admin \ No newline at end of file diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/pom.xml b/examples/authz/photoz/photoz-authz-policy/pom.xml similarity index 67% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/pom.xml rename to examples/authz/photoz/photoz-authz-policy/pom.xml index 6625a57537..f236a15efd 100755 --- a/examples/authz/photoz-uma/photoz-uma-authz-policy/pom.xml +++ b/examples/authz/photoz/photoz-authz-policy/pom.xml @@ -5,18 +5,18 @@ org.keycloak - keycloak-authz-photoz-uma-parent + keycloak-authz-photoz-parent 2.0.0.CR1-SNAPSHOT ../pom.xml - photoz-uma-authz-policy + photoz-authz-policy jar - Keycloak Authz: Examples - Photoz UMA Authz Rule-based Policy + Keycloak Authz: Examples - Photoz Authz Rule-based Policy - Photoz UMA Authz Rule-based Policies using JBoss Drools + Photoz Authz Rule-based Policies using JBoss Drools diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/META-INF/kmodule.xml b/examples/authz/photoz/photoz-authz-policy/src/main/resources/META-INF/kmodule.xml similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/META-INF/kmodule.xml rename to examples/authz/photoz/photoz-authz-policy/src/main/resources/META-INF/kmodule.xml diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl rename to examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.admin/Main.drl diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl rename to examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.resource.owner/Main.drl diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl rename to examples/authz/photoz/photoz-authz-policy/src/main/resources/com.photoz.authz.policy.user/Main.drl diff --git a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl similarity index 82% rename from examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl rename to examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl index d18746745d..8a6a772716 100644 --- a/examples/authz/photoz-uma/photoz-uma-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl +++ b/examples/authz/photoz/photoz-authz-policy/src/main/resources/com/photoz/authz/policy/contextual/Main.drl @@ -7,7 +7,7 @@ rule "Authorize Using Context Information" when $evaluation : Evaluation( $attributes: context.attributes, - $attributes.containsValue("kc.authz.context.authc.method", "otp"), + $attributes.containsValue("kc.identity.authc.method", "otp"), $attributes.containsValue("someAttribute", "you_can_access") ) then diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/pom.xml b/examples/authz/photoz/photoz-html5-client/pom.xml similarity index 83% rename from examples/authz/photoz-uma/photoz-uma-html5-client/pom.xml rename to examples/authz/photoz/photoz-html5-client/pom.xml index 39bf86fd73..31f6540751 100755 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/pom.xml +++ b/examples/authz/photoz/photoz-html5-client/pom.xml @@ -4,16 +4,16 @@ 4.0.0 org.keycloak - keycloak-authz-photoz-uma-parent + keycloak-authz-photoz-parent 2.0.0.CR1-SNAPSHOT ../pom.xml - photoz-uma-html5-client + photoz-html5-client war - Keycloak Authz: Photoz UMA HTML5 Client - Photoz UMA HTML5 Client + Keycloak Authz: Photoz HTML5 Client + Photoz HTML5 Client ${project.artifactId} diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/WEB-INF/web.xml b/examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml similarity index 85% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/WEB-INF/web.xml rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml index 98e4067f9a..a370557918 100644 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/WEB-INF/web.xml +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/WEB-INF/web.xml @@ -4,6 +4,6 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> - photoz-uma-html5-client + photoz-html5-client diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/index.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html similarity index 80% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/index.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html index b41383bd2f..f9375a299f 100755 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/index.html +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html @@ -12,6 +12,9 @@ + + + diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js new file mode 100755 index 0000000000..2990675a06 --- /dev/null +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js @@ -0,0 +1,168 @@ +var module = angular.module('photoz', ['ngRoute', 'ngResource']); + +var resourceServerId = 'photoz-restful-api'; +var apiUrl = window.location.origin + '/' + resourceServerId; + +angular.element(document).ready(function ($http) { + var keycloak = new Keycloak('keycloak.json'); + keycloak.init({onLoad: 'login-required'}).success(function () { + console.log('User is now authenticated.'); + + module.factory('Identity', function () { + return new Identity(keycloak); + }); + + angular.bootstrap(document, ["photoz"]); + }).error(function () { + window.location.reload(); + }); +}); + +module.config(function ($httpProvider, $routeProvider) { + $httpProvider.interceptors.push('authInterceptor'); + $routeProvider.when('/', { + templateUrl: 'partials/home.html', + controller: 'GlobalCtrl' + }).when('/album/create', { + templateUrl: 'partials/album/create.html', + controller: 'AlbumCtrl', + }).when('/album/:id', { + templateUrl: 'partials/album/detail.html', + controller: 'AlbumCtrl', + }).when('/admin/album', { + templateUrl: 'partials/admin/albums.html', + controller: 'AdminAlbumCtrl', + }).when('/profile', { + templateUrl: 'partials/profile.html', + controller: 'ProfileCtrl', + }); +}); + +module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Album, Identity) { + Album.query(function (albums) { + $scope.albums = albums; + }); + + $scope.Identity = Identity; + + $scope.deleteAlbum = function (album) { + new Album(album).$delete({id: album.id}, function () { + $route.reload(); + }); + } +}); + +module.controller('TokenCtrl', function ($scope, Identity) { + $scope.showRpt = function () { + document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.authorization.rpt), null, ' '); + } + + $scope.showAccessToken = function () { + document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(Identity.authc.token), null, ' '); + } + + $scope.requestEntitlements = function () { + Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {}); + } +}); + +module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) { + $scope.album = {}; + if ($routeParams.id) { + $scope.album = Album.get({id: $routeParams.id}); + } + $scope.create = function () { + var newAlbum = new Album($scope.album); + newAlbum.$save({}, function (data) { + $location.path('/'); + }); + }; +}); + +module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $location, Profile) { + $scope.profile = Profile.get(); +}); + +module.controller('AdminAlbumCtrl', function ($scope, $http, $route, AdminAlbum, Album) { + $scope.albums = {}; + $http.get(apiUrl + '/admin/album').success(function (data) { + $scope.albums = data; + }); + $scope.deleteAlbum = function (album) { + var newAlbum = new Album(album); + newAlbum.$delete({id: album.id}, function () { + $route.reload(); + }); + } +}); + +module.factory('Album', ['$resource', function ($resource) { + return $resource(apiUrl + '/album/:id'); +}]); + +module.factory('Profile', ['$resource', function ($resource) { + return $resource(apiUrl + '/profile'); +}]); + +module.factory('AdminAlbum', ['$resource', function ($resource) { + return $resource(apiUrl + '/admin/album/:id'); +}]); + +module.factory('authInterceptor', function ($q, $injector, $timeout, Identity) { + return { + request: function (request) { + document.getElementById("output").innerHTML = ''; + if (Identity.authorization && Identity.authorization.rpt && request.url.indexOf('/authorize') == -1) { + retries = 0; + request.headers.Authorization = 'Bearer ' + Identity.authorization.rpt; + } else { + request.headers.Authorization = 'Bearer ' + Identity.authc.token; + } + return request; + }, + responseError: function (rejection) { + var status = rejection.status; + + if (status == 403 || status == 401) { + var retry = (!rejection.config.retry || rejection.config.retry < 1); + + if (!retry) { + document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.'; + return $q.reject(rejection); + } + + if (rejection.config.url.indexOf('/authorize') == -1 && retry) { + var deferred = $q.defer(); + + // here is the authorization logic, which tries to obtain an authorization token from the server in case the resource server + // returns a 403 or 401. + Identity.authorization.authorize(rejection.headers('WWW-Authenticate')).then(function (rpt) { + deferred.resolve(rejection); + }, function () { + document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.'; + }, function () { + document.getElementById("output").innerHTML = 'Unexpected error from server.'; + }); + + var promise = deferred.promise; + + return promise.then(function (res) { + if (!res.config.retry) { + res.config.retry = 1; + } else { + res.config.retry++; + } + + var $http = $injector.get("$http"); + + return $http(res.config).then(function (response) { + return response; + }); + }); + } + } + + return $q.reject(rejection); + } + }; +}); \ No newline at end of file diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js new file mode 100644 index 0000000000..9a018e4747 --- /dev/null +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js @@ -0,0 +1,60 @@ +/* + * 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. + * + */ + +/** + * Creates an Identity object holding the information obtained from the access token issued by Keycloak, after a successful authentication, + * and a few utility methods to manage it. + */ +(function (window, undefined) { + var Identity = function (keycloak) { + this.loggedIn = true; + + this.claims = {}; + this.claims.name = keycloak.idTokenParsed.name; + + this.authc = {}; + this.authc.token = keycloak.token; + + this.logout = function () { + keycloak.logout(); + }; + + this.hasRole = function (name) { + if (keycloak && keycloak.hasRealmRole(name)) { + return true; + } + return false; + }; + + this.isAdmin = function () { + return this.hasRole("admin"); + }; + + this.authorization = new KeycloakAuthorization(keycloak); + } + + if ( typeof module === "object" && module && typeof module.exports === "object" ) { + module.exports = Identity; + } else { + window.Identity = Identity; + + if ( typeof define === "function" && define.amd ) { + define( "identity", [], function () { return Identity; } ); + } + } +})( window ); \ No newline at end of file diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/keycloak.json b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json similarity index 86% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/keycloak.json rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json index 3e73241763..4b4d193d4a 100644 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/keycloak.json +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json @@ -1,9 +1,9 @@ { - "realm": "photoz-uma", + "realm": "photoz", "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", - "resource" : "photoz-uma-html5-client", + "resource" : "photoz-html5-client", "public-client" : true, "use-resource-role-mappings": "false", "scope" : { diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular-resource.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular-resource.min.js rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-resource.min.js diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular-route.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular-route.min.js rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular-route.min.js diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/angular/angular.min.js rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/angular/angular.min.js diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/jwt-decode.min.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/jwt-decode.min.js similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/lib/jwt-decode.min.js rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/lib/jwt-decode.min.js diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/admin/albums.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/admin/albums.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/admin/albums.html diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/album/create.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/album/create.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/album/detail.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/album/detail.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/album/detail.html diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/home.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html similarity index 84% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/home.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html index dc10c92b6e..5e164b2fc7 100644 --- a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/home.html +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html @@ -1,4 +1,4 @@ -

Welcome To Photoz, {{Identity.claim.name}} [Sign Out]

+

Welcome To Photoz, {{Identity.claims.name}} [Sign Out]

Administration: [All Albums]


diff --git a/examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/profile.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/profile.html similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-html5-client/src/main/webapp/partials/profile.html rename to examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/profile.html diff --git a/examples/authz/photoz-uma/photoz-uma-realm.json b/examples/authz/photoz/photoz-realm.json similarity index 83% rename from examples/authz/photoz-uma/photoz-uma-realm.json rename to examples/authz/photoz/photoz-realm.json index 0230b616ac..047b09f10d 100644 --- a/examples/authz/photoz-uma/photoz-uma-realm.json +++ b/examples/authz/photoz/photoz-realm.json @@ -1,5 +1,5 @@ { - "realm": "photoz-uma", + "realm": "photoz", "enabled": true, "sslRequired": "external", "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", @@ -62,12 +62,12 @@ } }, { - "username": "service-account-photoz-uma-restful-api", + "username": "service-account-photoz-restful-api", "enabled": true, - "email": "service-account-photoz-uma-restful-api@placeholder.org", - "serviceAccountClientId": "photoz-uma-restful-api", + "email": "service-account-photoz-restful-api@placeholder.org", + "serviceAccountClientId": "photoz-restful-api", "clientRoles": { - "photoz-uma-restful-api" : ["uma_protection"] + "photoz-restful-api" : ["uma_protection"] } } ], @@ -85,25 +85,25 @@ }, "clients": [ { - "clientId": "photoz-uma-html5-client", + "clientId": "photoz-html5-client", "enabled": true, - "adminUrl": "/photoz-uma-html5-client", - "baseUrl": "/photoz-uma-html5-client", + "adminUrl": "/photoz-html5-client", + "baseUrl": "/photoz-html5-client", "publicClient": true, "redirectUris": [ - "/photoz-uma-html5-client/*" + "/photoz-html5-client/*" ], "webOrigins": [ "" ] }, { - "clientId": "photoz-uma-restful-api", + "clientId": "photoz-restful-api", "enabled": true, - "baseUrl": "/photoz-uma-restful-api", + "baseUrl": "/photoz-restful-api", "authorizationServicesEnabled" : true, "redirectUris": [ - "/photoz-uma-restful-api/*" + "/photoz-restful-api/*" ], "secret": "secret" } diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api-authz-service.json b/examples/authz/photoz/photoz-restful-api-authz-service.json similarity index 96% rename from examples/authz/photoz-uma/photoz-uma-restful-api-authz-service.json rename to examples/authz/photoz/photoz-restful-api-authz-service.json index 60a4cd5c9a..1d0356c5c7 100644 --- a/examples/authz/photoz-uma/photoz-uma-restful-api-authz-service.json +++ b/examples/authz/photoz/photoz-restful-api-authz-service.json @@ -46,7 +46,7 @@ "type": "drools", "config": { "mavenArtifactVersion": "2.0.0.CR1-SNAPSHOT", - "mavenArtifactId": "photoz-uma-authz-policy", + "mavenArtifactId": "photoz-authz-policy", "sessionName": "MainOwnerSession", "mavenArtifactGroupId": "org.keycloak", "moduleName": "PhotozAuthzOwnerPolicy", @@ -77,7 +77,7 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.authz.context.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}" + "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}" } }, { diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/pom.xml b/examples/authz/photoz/photoz-restful-api/pom.xml similarity index 90% rename from examples/authz/photoz-uma/photoz-uma-restful-api/pom.xml rename to examples/authz/photoz/photoz-restful-api/pom.xml index 0f04d950b9..eea3c17de5 100755 --- a/examples/authz/photoz-uma/photoz-uma-restful-api/pom.xml +++ b/examples/authz/photoz/photoz-restful-api/pom.xml @@ -5,16 +5,16 @@ org.keycloak - keycloak-authz-photoz-uma-parent + keycloak-authz-photoz-parent 2.0.0.CR1-SNAPSHOT ../pom.xml - photoz-uma-restful-api + photoz-restful-api war - Keycloak Authz: Photoz UMA RESTful API - Photoz UMA RESTful API + Keycloak Authz: Photoz RESTful API + Photoz RESTful API diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/ErrorResponse.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/PhotozApplication.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java rename to examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Photo.java diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/META-INF/beans.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/META-INF/beans.xml rename to examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/META-INF/persistence.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/META-INF/persistence.xml rename to examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json similarity index 60% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/keycloak.json rename to examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json index 528b178355..e7288274b0 100644 --- a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/resources/keycloak.json +++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/keycloak.json @@ -1,7 +1,7 @@ { - "realm": "photoz-uma", + "realm": "photoz", "auth-server-url": "http://localhost:8080/auth", - "resource": "photoz-uma-restful-api", + "resource": "photoz-restful-api", "credentials": { "secret": "secret" } diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml rename to examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json similarity index 84% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/keycloak.json rename to examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json index 8b21c95c95..95fb58bf70 100644 --- a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json @@ -1,15 +1,14 @@ { - "realm": "photoz-uma", + "realm": "photoz", "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required": "external", - "resource": "photoz-uma-restful-api", + "resource": "photoz-restful-api", "bearer-only" : true, "credentials": { "secret": "secret" }, "policy-enforcer": { - "user-managed-access" : {}, "paths": [ { "path" : "/album/*", @@ -31,10 +30,6 @@ { "method": "DELETE", "scopes" : ["urn:photoz.com:scopes:album:delete"] - }, - { - "method": "GET", - "scopes" : ["urn:photoz.com:scopes:album:view"] } ] }, diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml similarity index 100% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml rename to examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml diff --git a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/web.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml similarity index 92% rename from examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/web.xml rename to examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml index 92fd000d02..34cf6bd6a1 100644 --- a/examples/authz/photoz-uma/photoz-uma-restful-api/src/main/webapp/WEB-INF/web.xml +++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/web.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> - photoz-uma-restful-api + photoz-restful-api @@ -28,7 +28,7 @@ KEYCLOAK - photoz-uma + photoz diff --git a/examples/authz/photoz-uma/pom.xml b/examples/authz/photoz/pom.xml similarity index 69% rename from examples/authz/photoz-uma/pom.xml rename to examples/authz/photoz/pom.xml index f0482e460f..65259d86ef 100755 --- a/examples/authz/photoz-uma/pom.xml +++ b/examples/authz/photoz/pom.xml @@ -10,15 +10,15 @@ ../pom.xml - keycloak-authz-photoz-uma-parent + keycloak-authz-photoz-parent pom - Keycloak Authz: PhotoZ UMA Example Application Parent + Keycloak Authz: PhotoZ Example Application Parent PhotoZ Example Application - photoz-uma-restful-api - photoz-uma-html5-client - photoz-uma-authz-policy + photoz-restful-api + photoz-html5-client + photoz-authz-policy diff --git a/examples/authz/pom.xml b/examples/authz/pom.xml index 318d01eabf..e9eb4eb0b9 100755 --- a/examples/authz/pom.xml +++ b/examples/authz/pom.xml @@ -22,7 +22,7 @@ - photoz-uma + photoz servlet-authz hello-world hello-world-authz-service diff --git a/examples/authz/servlet-authz/README.md b/examples/authz/servlet-authz/README.md index df528704dc..f93acb52ca 100644 --- a/examples/authz/servlet-authz/README.md +++ b/examples/authz/servlet-authz/README.md @@ -14,7 +14,7 @@ This application will also show you how to create a dynamic menu with the permis ## Create the Example Realm and a Resource Server -Considering that your AuthZ Server is up and running, log in to the Keycloak Administration Console. +Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console. Now, create a new realm based on the following configuration file: @@ -25,26 +25,30 @@ into Keycloak, check the Keycloak's reference documentation. After importing that file, you'll have a new realm called ``servlet-authz``. -Now, let's import another configuration using the Administration Console in order to configure the ``servlet-authz-app`` client application as a resource server with all resources, scopes, permissions and policies. +Now, let's import another configuration using the Administration Console in order to configure the client application ``servlet-authz-app`` as a resource server with all resources, scopes, permissions and policies. -Click on ``Authorization`` on the left side menu. Click on the ``Create`` button on the top of the resource server table. This will -open the page that allows you to create a new resource server. +Click on ``Clients`` on the left side menu. Click on the ``servlet-authz-app`` on the client listing page. This will +open the ``Client Details`` page. Once there, click on the `Authorization` tab. Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at: examples/authz/servlet-authz/servlet-authz-app-config.json -Now click ``Upload`` and a new resource server will be created based on the ``servlet-authz-app`` client application. +Now click ``Upload`` and the resource server will be updated accordingly. ## Deploy and Run the Example Applications -To deploy the example applications, follow these steps: +To deploy the example application, follow these steps: cd examples/authz/servlet-authz - mvn wildfly:deploy + mvn clean package wildfly:deploy +Now, try to access the client application using the following URL: + + http://localhost:8080/servlet-authz-app + If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials: -* username: jdoe / password: jdoe (premium user) -* username: alice / password: alice (regular user) -* username: admin / password: admin (administrator) \ No newline at end of file +* username: jdoe / password: jdoe +* username: alice / password: alice +* username: admin / password: admin \ No newline at end of file diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json index 7f37597417..eaffea8d19 100644 --- a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json @@ -8,5 +8,7 @@ "credentials": { "secret": "secret" }, - "policy-enforcer": {} + "policy-enforcer": { + "on-deny-redirect-to" : "/servlet-authz-app/accessDenied.jsp" + } } \ No newline at end of file diff --git a/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp b/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp index be85c22004..6f25023af0 100644 --- a/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/accessDenied.jsp @@ -1,8 +1,6 @@ -<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> -<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> -

You can not access this resource. Click ">here to log in as a different user.

+

You can not access this resource.

+ <%@include file="logout-include.jsp"%> \ No newline at end of file diff --git a/examples/authz/servlet-authz/src/main/webapp/index.jsp b/examples/authz/servlet-authz/src/main/webapp/index.jsp index 118f142c59..78c54440b1 100755 --- a/examples/authz/servlet-authz/src/main/webapp/index.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/index.jsp @@ -1,6 +1,4 @@ <%@page import="org.keycloak.AuthorizationContext" %> -<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> -<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> <%@ page import="org.keycloak.KeycloakSecurityContext" %> <%@ page import="org.keycloak.representations.authorization.Permission" %> @@ -11,8 +9,7 @@ -

Click ">here to logout.

+ <%@include file="logout-include.jsp"%>

This is a public resource. Try to access one of these protected resources:

Dynamic Menu

diff --git a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp new file mode 100644 index 0000000000..95365ea195 --- /dev/null +++ b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp @@ -0,0 +1,11 @@ +<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> +<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> +<% + String scheme = request.getScheme(); + String host = request.getServerName(); + int port = request.getServerPort(); + String contextPath = request.getContextPath(); + String redirectUri = scheme + "://" + host + ":" + port + contextPath; +%> +

Click ">here to logout.

\ No newline at end of file diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp index 554b250467..5946cd660c 100644 --- a/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp @@ -1,8 +1,6 @@ -<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> -<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> -

Only Administrators can access this page. Click ">here to logout.

+

Only Administrators can access this page.

+ <%@include file="../../logout-include.jsp"%> \ No newline at end of file diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp index 7240a98be2..1473d223f3 100644 --- a/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp @@ -1,6 +1,4 @@ <%@page import="org.keycloak.AuthorizationContext" %> -<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> -<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> <%@ page import="org.keycloak.KeycloakSecurityContext" %> <% @@ -10,8 +8,8 @@ -

Any authenticated user can access this page. Click ">here to logout.

+

Any authenticated user can access this page.

+<%@include file="../logout-include.jsp"%>

Here is a dynamic menu built from the permissions returned by the server:

diff --git a/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp b/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp index f1725730f1..9244f9ca5e 100644 --- a/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp @@ -1,9 +1,6 @@ -<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> -<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> -

Only for premium users. Click ">here to logout.

- +

Only for premium users.

+<%@include file="../../logout-include.jsp"%> \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java index 1c31fcc7f1..84e52954c3 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java @@ -463,7 +463,7 @@ public class ResourceServerService { "\n" + "// using attributes from the evaluation context to obtain the realm\n" + "var contextAttributes = context.getAttributes();\n" + - "var realmName = contextAttributes.getValue('kc.authz.context.authc.realm').asString(0);\n" + + "var realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n" + "\n" + "// using attributes from the identity to obtain the issuer\n" + "var identity = context.getIdentity();\n" + diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java index eaa8c78de8..ad154a6bf7 100644 --- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java +++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java @@ -106,7 +106,10 @@ public class AuthorizationTokenService { List entitlements = Permissions.allPermits(results); if (entitlements.isEmpty()) { - asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)); + asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN) + .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN))) + .allowedOrigins(identity.getAccessToken()) + .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); } else { AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())); asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken()) @@ -217,12 +220,14 @@ public class AuthorizationTokenService { } private PermissionTicket verifyPermissionTicket(AuthorizationRequest request) { - if (!Tokens.verifySignature(request.getTicket(), getRealm().getPublicKey())) { + String ticketString = request.getTicket(); + + if (ticketString == null || !Tokens.verifySignature(ticketString, getRealm().getPublicKey())) { throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN); } try { - PermissionTicket ticket = new JWSInput(request.getTicket()).readJsonContent(PermissionTicket.class); + PermissionTicket ticket = new JWSInput(ticketString).readJsonContent(PermissionTicket.class); if (!ticket.isActive()) { throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN); diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java index bc967b9af4..fc929ec5d9 100644 --- a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java +++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java @@ -57,23 +57,23 @@ public class KeycloakEvaluationContext implements EvaluationContext { public Attributes getAttributes() { HashMap> attributes = new HashMap<>(); - attributes.put("kc.authz.context.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date()))); - attributes.put("kc.authz.context.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr())); - attributes.put("kc.authz.context.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost())); + attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date()))); + attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr())); + attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost())); AccessToken accessToken = this.identity.getAccessToken(); if (accessToken != null) { - attributes.put("kc.authz.context.client_id", Arrays.asList(accessToken.getIssuedFor())); + attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor())); } List userAgents = this.keycloakSession.getContext().getRequestHeaders().getRequestHeader("User-Agent"); if (userAgents != null) { - attributes.put("kc.authz.context.client.user_agent", userAgents); + attributes.put("kc.client.user_agent", userAgents); } - attributes.put("kc.authz.context.authc.realm", Arrays.asList(this.keycloakSession.getContext().getRealm().getName())); + attributes.put("kc.realm.name", Arrays.asList(this.keycloakSession.getContext().getRealm().getName())); return Attributes.from(attributes); } diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java index 983646b6a6..df6f54d098 100644 --- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java +++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java @@ -80,8 +80,9 @@ public class EntitlementService { this.authorization = authorization; } + @Path("{resource_server_id}") @OPTIONS - public Response authorizePreFlight() { + public Response authorizePreFlight(@PathParam("resource_server_id") String resourceServerId) { return Cors.add(this.request, Response.ok()).auth().preflight().build(); } @@ -118,7 +119,10 @@ public class EntitlementService { List entitlements = Permissions.allPermits(results); if (entitlements.isEmpty()) { - asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)); + asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN) + .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN))) + .allowedOrigins(identity.getAccessToken()) + .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); } else { asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); } diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java index 240fafad9e..43204b83f9 100644 --- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java +++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java @@ -26,6 +26,7 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.permission.ResourcePermission; import org.keycloak.authorization.policy.evaluation.Result; +import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.representations.authorization.Permission; @@ -58,9 +59,10 @@ public final class Permissions { public static List all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) { List permissions = new ArrayList<>(); StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceStore resourceStore = storeFactory.getResourceStore(); - storeFactory.getResourceStore().findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); - storeFactory.getResourceStore().findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); + resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); + resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource))); return permissions; } 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 32161a4827..c74abf0e52 100755 --- a/services/src/main/java/org/keycloak/services/resources/JsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/JsResource.java @@ -44,35 +44,82 @@ 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)); - - return Response.ok(inputStream).type("text/javascript").cacheControl(cacheControl).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).build(); - } + public Response getKeycloakJs() { + return getJs("keycloak.js"); } @GET @Path("/{version}/keycloak.js") @Produces("text/javascript") - public Response getJsWithVersion(@PathParam("version") String version) { + public Response getKeycloakJsWithVersion(@PathParam("version") String version) { if (!version.equals(Version.RESOURCES_VERSION)) { return Response.status(Response.Status.NOT_FOUND).build(); } - return getJs(); + return getKeycloakJs(); } @GET @Path("/keycloak.min.js") @Produces("text/javascript") - public Response getMinJs() { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("keycloak.min.js"); + public Response getKeycloakMinJs() { + 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) { CacheControl cacheControl = new CacheControl(); cacheControl.setNoTransform(false); @@ -83,16 +130,4 @@ public class JsResource { 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(); - } - } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java index 8876f3088a..0786eabec5 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java @@ -261,7 +261,7 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest config.put("code", "var contextAttributes = $evaluation.getContext().getAttributes();" + - "var networkAddress = contextAttributes.getValue('kc.authz.context.client.network.ip_address');" + + "var networkAddress = contextAttributes.getValue('kc.client.network.ip_address');" + "if ('127.0.0.1'.equals(networkAddress.asInetAddress(0).getHostAddress())) {" + "$evaluation.grant();" + "}"); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js index e07ed1af54..ecbe9d533e 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js @@ -1044,7 +1044,7 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio custom: true }, { - key : "kc.authz.context.authc.method", + key : "kc.identity.authc.method", name : "Authentication Method", values: [ { @@ -1062,23 +1062,23 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio ] }, { - key : "kc.authz.context.authc.realm", + key : "kc.realm.name", name : "Realm" }, { - key : "kc.authz.context.time.date_time", + key : "kc.time.date_time", name : "Date/Time (MM/dd/yyyy hh:mm:ss)" }, { - key : "kc.authz.context.client.network.ip_address", + key : "kc.client.network.ip_address", name : "Client IPv4 Address" }, { - key : "kc.authz.context.client.network.host", + key : "kc.client.network.host", name : "Client Host" }, { - key : "kc.authz.context.client.user_agent", + key : "kc.client.user_agent", name : "Client/User Agent" } ]; diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html index 4919fe6c9c..d03eefbcda 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html @@ -5,7 +5,7 @@