[KEYCLOAK-7162] - Expose WWW-Authenticate Header when using CORS

This commit is contained in:
pedroigor 2018-04-13 15:48:09 -03:00 committed by Hynek Mlnařík
parent 527d6ca4d8
commit c3d297dd05
5 changed files with 87 additions and 3 deletions

View file

@ -102,6 +102,14 @@ public class AuthenticatedActionsHandler {
KeycloakSecurityContext securityContext = facade.getSecurityContext();
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
String exposeHeaders = deployment.getCorsExposedHeaders();
if (deployment.getPolicyEnforcer() != null) {
if (exposeHeaders != null) {
exposeHeaders += ",";
}
exposeHeaders += "WWW-Authenticate";
}
String requestOrigin = UriUtils.getOrigin(facade.getRequest().getURI());
log.debugv("Origin: {0} uri: {1}", origin, facade.getRequest().getURI());
if (securityContext != null && origin != null && !origin.equals(requestOrigin)) {

View file

@ -26,6 +26,14 @@
"clientRoles": {
"realm-management": [ "realm-admin" ]
}
},
{
"username": "service-account-cors-database-service",
"enabled": true,
"serviceAccountClientId": "cors-database-service",
"clientRoles": {
"cors-database-service" : ["uma_protection"]
}
}
],
"roles" : {
@ -54,6 +62,49 @@
"webOrigins": [
"http://localhost:8080"
]
},
{
"clientId": "cors-database-service",
"secret" : "secret",
"enabled": true,
"baseUrl": "http://localhost:8080/cors-database/products",
"redirectUris": [
"http://localhost:8080/cors-database/*"
],
"webOrigins": [
"http://localhost:8080"
],
"authorizationServicesEnabled" : true,
"authorizationSettings" : {
"allowRemoteResourceManagement" : false,
"policyEnforcementMode" : "ENFORCING",
"resources" : [ {
"name" : "Default Resource",
"uri" : "/*",
"type" : "default"
} ],
"policies" : [ {
"name" : "Default Policy",
"description" : "A policy that grants access only for users within this realm",
"type" : "js",
"logic" : "POSITIVE",
"decisionStrategy" : "AFFIRMATIVE",
"config" : {
"code" : "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
}
}, {
"name" : "Default Permission",
"description" : "A permission that applies to the default resource type",
"type" : "resource",
"logic" : "POSITIVE",
"decisionStrategy" : "UNANIMOUS",
"config" : {
"defaultResourceType" : "default",
"applyPolicies" : "[\"Default Policy\"]"
}
} ],
"scopes" : [ ]
}
}
],
"clientScopeMappings": {

View file

@ -47,6 +47,7 @@ public class ProductService {
rtn.add("ipod");
response.addHeader("X-Custom1", "some-value");
response.addHeader("WWW-Authenticate", "some-value");
return rtn;
}
}

View file

@ -6,5 +6,11 @@
"bearer-only" : true,
"ssl-required": "external",
"enable-cors": true,
"cors-exposed-headers": "X-Custom1"
"cors-exposed-headers": "X-Custom1",
"credentials": {
"secret": "secret"
},
"policy-enforcer": {
"enforcement-mode": "DISABLED"
}
}

View file

@ -17,11 +17,15 @@
package org.keycloak.testsuite.adapter.example.cors;
import org.jboss.arquillian.container.test.api.Deployer;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
@ -53,6 +57,9 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
public static final String AUTH_SERVER_HOST = "localhost-auth";
private static String hostBackup;
@ArquillianResource
private Deployer deployer;
@Page
@JavascriptBrowser
private AngularCorsProductTestApp jsDriverAngularCorsProductPage;
@ -66,7 +73,7 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
return exampleDeployment(AngularCorsProductTestApp.CLIENT_ID);
}
@Deployment(name = CorsDatabaseServiceTestApp.DEPLOYMENT_NAME)
@Deployment(name = CorsDatabaseServiceTestApp.DEPLOYMENT_NAME, managed = false)
private static WebArchive corsDatabaseServiceExample() throws IOException {
return exampleDeployment(CorsDatabaseServiceTestApp.CLIENT_ID);
}
@ -74,7 +81,17 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
@Override
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
testRealms.add(
loadRealm(new File(EXAMPLES_HOME_DIR + "/cors/cors-realm.json")));
loadRealm(new File(TEST_APPS_HOME_DIR + "/cors/cors-realm.json")));
}
@Before
public void onBefore() {
deployer.deploy(CorsDatabaseServiceTestApp.DEPLOYMENT_NAME);
}
@After
public void onAfter() {
deployer.undeploy(CorsDatabaseServiceTestApp.DEPLOYMENT_NAME);
}
static{
@ -100,6 +117,7 @@ public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdap
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("ipad");
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("ipod");
waitUntilElement(jsDriverAngularCorsProductPage.getHeaders()).text().contains("\"x-custom1\":\"some-value\"");
waitUntilElement(jsDriverAngularCorsProductPage.getHeaders()).text().contains("\"www-authenticate\":\"some-value\"");
jsDriverAngularCorsProductPage.loadRoles();
waitUntilElement(jsDriverAngularCorsProductPage.getOutput()).text().contains("user");