keycloak-uncached
Changes
examples/cors/cors-realm.json 21(+19 -2)
Details
diff --git a/examples/cors/angular-product-app/src/main/webapp/index.html b/examples/cors/angular-product-app/src/main/webapp/index.html
index ebd3968..2e05d7f 100755
--- a/examples/cors/angular-product-app/src/main/webapp/index.html
+++ b/examples/cors/angular-product-app/src/main/webapp/index.html
@@ -34,6 +34,24 @@
</tbody>
</table>
</div>
+ <div>
+ <h2><span>Realm Roles</span></h2>
+ <button type="submit" data-ng-click="loadRoles()">load Roles</button>
+ <button type="submit" data-ng-click="addRole()">Add Role</button>
+ <button type="submit" data-ng-click="deleteRole()">Delete Role</button>
+ <table class="table" data-ng-show="roles.length > 0">
+ <thead>
+ <tr>
+ <th>Role Listing</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="r in roles">
+ <td>{{r.name}}</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
</body>
</html>
diff --git a/examples/cors/angular-product-app/src/main/webapp/js/app.js b/examples/cors/angular-product-app/src/main/webapp/js/app.js
index a9d879e..50b68bc 100755
--- a/examples/cors/angular-product-app/src/main/webapp/js/app.js
+++ b/examples/cors/angular-product-app/src/main/webapp/js/app.js
@@ -29,6 +29,7 @@ angular.element(document).ready(function ($http) {
module.controller('GlobalCtrl', function($scope, $http) {
$scope.products = [];
+ $scope.roles = [];
$scope.reloadData = function() {
$http.get("http://localhost-db:8080/database/products").success(function(data) {
$scope.products = angular.fromJson(data);
@@ -36,6 +37,25 @@ module.controller('GlobalCtrl', function($scope, $http) {
});
};
+ $scope.loadRoles = function() {
+ $http.query("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles").success(function(data) {
+ $scope.roles = angular.fromJson(data);
+
+ });
+
+ };
+ $scope.addRole = function() {
+ $http.post("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles", {name: 'stuff'}).success(function() {
+ $scope.loadRoles();
+ });
+
+ };
+ $scope.deleteRole = function() {
+ $http.delete("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles/stuff").success(function() {
+ $scope.loadRoles();
+ });
+
+ };
$scope.logout = logout;
});
diff --git a/examples/cors/angular-product-app/src/main/webapp/keycloak.json b/examples/cors/angular-product-app/src/main/webapp/keycloak.json
index 7ea2954..6b94b27 100755
--- a/examples/cors/angular-product-app/src/main/webapp/keycloak.json
+++ b/examples/cors/angular-product-app/src/main/webapp/keycloak.json
@@ -1,5 +1,5 @@
{
- "realm" : "cors-realm",
+ "realm" : "cors",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost-auth:8080/auth",
"ssl-not-required" : true,
examples/cors/cors-realm.json 21(+19 -2)
diff --git a/examples/cors/cors-realm.json b/examples/cors/cors-realm.json
index 32705aa..9b3da42 100755
--- a/examples/cors/cors-realm.json
+++ b/examples/cors/cors-realm.json
@@ -1,5 +1,5 @@
{
- "realm": "cors-realm",
+ "realm": "cors",
"enabled": true,
"accessTokenLifespan": 3000,
"accessCodeLifespan": 10,
@@ -57,5 +57,22 @@
"http://localhost:8080"
]
}
- ]
+ ],
+ "applicationRoleMappings": {
+ "realm-management": [
+ {
+ "username": "bburke@redhat.com",
+ "roles": ["realm-admin"]
+ }
+ ]
+ },
+ "applicationScopeMappings": {
+ "realm-management": [
+ {
+ "client": "angular-product",
+ "roles": ["realm-admin"]
+ }
+ ]
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/managers/Auth.java b/services/src/main/java/org/keycloak/services/managers/Auth.java
index 1966f43..d549aa2 100755
--- a/services/src/main/java/org/keycloak/services/managers/Auth.java
+++ b/services/src/main/java/org/keycloak/services/managers/Auth.java
@@ -35,6 +35,15 @@ public class Auth {
this.client = client;
}
+ public Auth(RealmModel realm, AccessToken token, UserModel user, ClientModel client) {
+ this.cookie = false;
+ this.token = token;
+ this.realm = realm;
+
+ this.user = user;
+ this.client = client;
+ }
+
public boolean isCookie() {
return cookie;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index c23858d..513d9c0 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -221,7 +221,7 @@ public class AuthenticationManager {
return null;
}
- return new AuthResult(user, session);
+ return new AuthResult(user, session, token);
} catch (VerificationException e) {
logger.info("Failed to verify identity token", e);
}
@@ -361,10 +361,12 @@ public class AuthenticationManager {
public class AuthResult {
private final UserModel user;
private final UserSessionModel session;
+ private final AccessToken token;
- public AuthResult(UserModel user, UserSessionModel session) {
+ public AuthResult(UserModel user, UserSessionModel session, AccessToken token) {
this.user = user;
this.session = session;
+ this.token = token;
}
public UserSessionModel getSession() {
@@ -374,6 +376,10 @@ public class AuthenticationManager {
public UserModel getUser() {
return user;
}
+
+ public AccessToken getToken() {
+ return token;
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 1543087..66a49e0 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -103,7 +103,8 @@ public class RealmManager {
}
protected void setupAdminConsole(RealmModel realm) {
- ApplicationModel adminConsole = new ApplicationManager(this).createApplication(realm, Constants.ADMIN_CONSOLE_APPLICATION);
+ ApplicationModel adminConsole = realm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
+ if (adminConsole == null) adminConsole = new ApplicationManager(this).createApplication(realm, Constants.ADMIN_CONSOLE_APPLICATION);
String baseUrl = contextPath + "/admin/" + realm.getName() + "/console";
adminConsole.setBaseUrl(baseUrl + "/index.html");
adminConsole.setEnabled(true);
@@ -113,12 +114,10 @@ public class RealmManager {
RoleModel adminRole;
if (realm.getName().equals(Config.getAdminRealm())) {
adminRole = realm.getRole(AdminRoles.ADMIN);
+ realm.addScopeMapping(adminConsole, adminRole);
} else {
- ApplicationModel realmApp = realm.getApplicationByName(getRealmAdminApplicationName(realm));
- adminRole = realmApp.getRole(AdminRoles.REALM_ADMIN);
-
+ // security roles are defined in application for the realm.
}
- realm.addScopeMapping(adminConsole, adminRole);
}
public String getMasterRealmAdminApplicationName(RealmModel realm) {
@@ -265,7 +264,11 @@ public class RealmManager {
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(identitySession));
- ApplicationModel realmAdminApp = applicationManager.createApplication(realm, getRealmAdminApplicationName(realm));
+ String realmAdminApplicationName = getRealmAdminApplicationName(realm);
+ ApplicationModel realmAdminApp = realm.getApplicationByName(realmAdminApplicationName);
+ if (realmAdminApp == null) {
+ realmAdminApp = applicationManager.createApplication(realm, realmAdminApplicationName);
+ }
RoleModel adminRole = realmAdminApp.addRole(AdminRoles.REALM_ADMIN);
realmAdminApp.setBearerOnly(true);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index 1c01d84..43fecc8 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -1,6 +1,9 @@
package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.DefaultOptionsMethodException;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.UnauthorizedException;
@@ -17,10 +20,12 @@ import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
+import org.keycloak.services.resources.Cors;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
@@ -39,6 +44,12 @@ public class AdminRoot {
@Context
protected UriInfo uriInfo;
+ @Context
+ protected HttpRequest request;
+
+ @Context
+ protected HttpResponse response;
+
protected AppAuthManager authManager;
protected TokenManager tokenManager;
@@ -127,7 +138,7 @@ public class AdminRoot {
if (consoleApp == null) {
throw new NotFoundException("Could not find admin console application");
}
- Auth auth = new Auth(realm, authResult.getUser(), consoleApp);
+ Auth auth = new Auth(realm, token, authResult.getUser(), consoleApp);
return auth;
@@ -143,10 +154,18 @@ public class AdminRoot {
@Path("realms")
public RealmsAdminResource getRealmsAdmin(@Context final HttpHeaders headers) {
+ if (request.getHttpMethod().equalsIgnoreCase("OPTIONS")) {
+ Response response = Cors.add(request, Response.ok()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build();
+ throw new WebApplicationException(response);
+ }
+
Auth auth = authenticateRealmAdminRequest(headers);
if (auth != null) {
logger.info("authenticated admin access for: " + auth.getUser().getLoginName());
}
+
+ Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
+
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager);
ResteasyProviderFactory.getInstance().injectProperties(adminResource);
//resourceContext.initResource(adminResource);
diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java
index 9dbcd39..dcf3499 100755
--- a/services/src/main/java/org/keycloak/services/resources/Cors.java
+++ b/services/src/main/java/org/keycloak/services/resources/Cors.java
@@ -9,7 +9,9 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.ClientModel;
+import org.keycloak.representations.AccessToken;
import org.keycloak.util.CollectionUtil;
/**
@@ -33,7 +35,7 @@ public class Cors {
private HttpRequest request;
- private ResponseBuilder response;
+ private ResponseBuilder builder;
private Set<String> allowedOrigins;
private Set<String> allowedMethods;
private Set<String> exposedHeaders;
@@ -43,13 +45,21 @@ public class Cors {
public Cors(HttpRequest request, ResponseBuilder response) {
this.request = request;
- this.response = response;
+ this.builder = response;
+ }
+
+ public Cors(HttpRequest request) {
+ this.request = request;
}
public static Cors add(HttpRequest request, ResponseBuilder response) {
return new Cors(request, response);
}
+ public static Cors add(HttpRequest request) {
+ return new Cors(request);
+ }
+
public Cors preflight() {
preflight = true;
return this;
@@ -67,6 +77,13 @@ public class Cors {
return this;
}
+ public Cors allowedOrigins(AccessToken token) {
+ if (token != null) {
+ allowedOrigins = token.getAllowedOrigins();
+ }
+ return this;
+ }
+
public Cors allowedMethods(String... allowedMethods) {
this.allowedMethods = new HashSet<String>(Arrays.asList(allowedMethods));
return this;
@@ -80,35 +97,66 @@ public class Cors {
public Response build() {
String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER);
if (origin == null) {
- return response.build();
+ return builder.build();
}
if (!preflight && (allowedOrigins == null || !allowedOrigins.contains(origin))) {
- return response.build();
+ return builder.build();
}
- response.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+ builder.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
if (allowedMethods != null) {
- response.header(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
+ builder.header(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
} else {
- response.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
+ builder.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
}
if (exposedHeaders != null) {
- response.header(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
+ builder.header(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
}
- response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
+ builder.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
if (auth) {
- response.header(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
+ builder.header(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
} else {
- response.header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
+ builder.header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
+ }
+
+ builder.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
+
+ return builder.build();
+ }
+ public void build(HttpResponse response) {
+ String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER);
+ if (origin == null) {
+ return;
}
- response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
+ if (!preflight && (allowedOrigins == null || !allowedOrigins.contains(origin))) {
+ return;
+ }
+
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+
+ if (allowedMethods != null) {
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
+ } else {
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
+ }
+
+ if (exposedHeaders != null) {
+ response.getOutputHeaders().add(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
+ }
+
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
+ if (auth) {
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
+ } else {
+ response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
+ }
- return response.build();
+ response.getOutputHeaders().add(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 7bec7a7..7c8705b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -158,12 +158,10 @@ public class AccountTest {
});
}
- /*
@Test
public void forever() throws Exception{
while (true) Thread.sleep(5000);
}
- */
@Test
public void returnToAppFromQueryParam() {