From 7659b1aa18701e7fa5935a7acb8f191e13c6fade Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Wed, 27 May 2015 10:03:17 +0200 Subject: [PATCH 1/6] KEYCLOAK-1103 - First draft --- .../proxy/ConstraintAuthorizationHandler.java | 45 ++++++++++++------- .../proxy/ConstraintMatcherHandler.java | 36 ++++++++++++++- .../java/org/keycloak/proxy/ProxyConfig.java | 25 +++++++++-- .../keycloak/proxy/ProxyServerBuilder.java | 22 ++++++--- .../java/org/keycloak/proxy/SecurityInfo.java | 6 ++- 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java index 58186dc298..5108149848 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java @@ -6,43 +6,58 @@ import io.undertow.util.HttpString; import org.keycloak.adapters.undertow.KeycloakUndertowAccount; import org.keycloak.representations.IDToken; +import java.util.HashMap; +import java.util.Map; + /** * @author Bill Burke * @version $Revision: 1 $ */ public class ConstraintAuthorizationHandler implements HttpHandler { - public static final HttpString KEYCLOAK_SUBJECT = new HttpString("KEYCLOAK-SUBJECT"); - public static final HttpString KEYCLOAK_USERNAME = new HttpString("KEYCLOAK-USERNAME"); - public static final HttpString KEYCLOAK_EMAIL = new HttpString("KEYCLOAK-EMAIL"); - public static final HttpString KEYCLOAK_NAME = new HttpString("KEYCLOAK-NAME"); - public static final HttpString KEYCLOAK_ACCESS_TOKEN = new HttpString("KEYCLOAK-ACCESS-TOKEN"); + private final Map httpHeaderNames; protected HttpHandler next; protected String errorPage; protected boolean sendAccessToken; - public ConstraintAuthorizationHandler(HttpHandler next, String errorPage, boolean sendAccessToken) { + public ConstraintAuthorizationHandler(HttpHandler next, String errorPage, boolean sendAccessToken, Map headerNames) { this.next = next; this.errorPage = errorPage; this.sendAccessToken = sendAccessToken; + + this.httpHeaderNames = new HashMap<>(); + this.httpHeaderNames.put("KEYCLOAK_SUBJECT", new HttpString(headerNames.getOrDefault("keycloak-subject", "KEYCLOAK_SUBJECT"))); + this.httpHeaderNames.put("KEYCLOAK_USERNAME", new HttpString(headerNames.getOrDefault("keycloak-username", "KEYCLOAK_USERNAME"))); + this.httpHeaderNames.put("KEYCLOAK_EMAIL", new HttpString(headerNames.getOrDefault("keycloak-email", "KEYCLOAK_EMAIL"))); + this.httpHeaderNames.put("KEYCLOAK_NAME", new HttpString(headerNames.getOrDefault("keycloak-name", "KEYCLOAK_NAME"))); + this.httpHeaderNames.put("KEYCLOAK_ACCESS_TOKEN", new HttpString(headerNames.getOrDefault("keycloak-access-token", "KEYCLOAK_ACCESS_TOKEN"))); } @Override public void handleRequest(HttpServerExchange exchange) throws Exception { + KeycloakUndertowAccount account = (KeycloakUndertowAccount)exchange.getSecurityContext().getAuthenticatedAccount(); + SingleConstraintMatch match = exchange.getAttachment(ConstraintMatcherHandler.CONSTRAINT_KEY); if (match == null || (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.AUTHENTICATE)) { authenticatedRequest(account, exchange); return; } + if (match != null) { - for (String role : match.getRequiredRoles()) { - if (account.getRoles().contains(role)) { - authenticatedRequest(account, exchange); - return; + if(SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED.equals(match.getEmptyRoleSemantic())) { + authenticatedRequest(account, exchange); + return; + } else { + for (String role : match.getRequiredRoles()) { + if (account.getRoles().contains(role)) { + authenticatedRequest(account, exchange); + return; + } } } } + if (errorPage != null) { exchange.setRequestPath(errorPage); exchange.setRelativePath(errorPage); @@ -61,20 +76,20 @@ public class ConstraintAuthorizationHandler implements HttpHandler { IDToken idToken = account.getKeycloakSecurityContext().getToken(); if (idToken == null) return; if (idToken.getSubject() != null) { - exchange.getRequestHeaders().put(KEYCLOAK_SUBJECT, idToken.getSubject()); + exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_SUBJECT"), idToken.getSubject()); } if (idToken.getPreferredUsername() != null) { - exchange.getRequestHeaders().put(KEYCLOAK_USERNAME, idToken.getPreferredUsername()); + exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_USERNAME"), idToken.getPreferredUsername()); } if (idToken.getEmail() != null) { - exchange.getRequestHeaders().put(KEYCLOAK_EMAIL, idToken.getEmail()); + exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_EMAIL"), idToken.getEmail()); } if (idToken.getName() != null) { - exchange.getRequestHeaders().put(KEYCLOAK_NAME, idToken.getName()); + exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_NAME"), idToken.getName()); } if (sendAccessToken) { - exchange.getRequestHeaders().put(KEYCLOAK_ACCESS_TOKEN, account.getKeycloakSecurityContext().getTokenString()); + exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_ACCESS_TOKEN"), account.getKeycloakSecurityContext().getTokenString()); } } next.handleRequest(exchange); diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java index 998302dea1..f1347bb50d 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java @@ -1,11 +1,12 @@ package org.keycloak.proxy; -import io.undertow.security.handlers.AuthenticationConstraintHandler; +import io.undertow.security.api.AuthenticationMechanism; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.util.AttachmentKey; import org.jboss.logging.Logger; -import org.keycloak.KeycloakSecurityContext; + +import java.util.List; /** * @author Bill Burke @@ -47,10 +48,41 @@ public class ConstraintMatcherHandler implements HttpHandler { } return; } + + if (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED) { + + boolean successfulAuthenticatedMethodFound = isSuccessfulAuthenticatedMethodFound(exchange); + + if(successfulAuthenticatedMethodFound) { + //in case of authenticated we go for injecting headers + exchange.putAttachment(CONSTRAINT_KEY, match); + securedHandler.handleRequest(exchange); + return; + } else { + //in case of not authenticated we just show the resource + unsecuredHandler.handleRequest(exchange); + return; + } + } + log.debug("found constraint"); exchange.getSecurityContext().setAuthenticationRequired(); exchange.putAttachment(CONSTRAINT_KEY, match); securedHandler.handleRequest(exchange); } + + private boolean isSuccessfulAuthenticatedMethodFound(HttpServerExchange exchange) { + boolean successfulAuthenticatedMethodFound = false; + List authenticationMechanisms = exchange.getSecurityContext().getAuthenticationMechanisms(); + + for (AuthenticationMechanism authenticationMechanism : authenticationMechanisms) { + AuthenticationMechanism.AuthenticationMechanismOutcome authenticationMechanismOutcome = + authenticationMechanism.authenticate(exchange, exchange.getSecurityContext()); + if(authenticationMechanismOutcome.equals(AuthenticationMechanism.AuthenticationMechanismOutcome.AUTHENTICATED)) { + successfulAuthenticatedMethodFound = true; + } + } + return successfulAuthenticatedMethodFound; + } } diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java index dd41a05d6a..e0cdbab763 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java @@ -3,10 +3,7 @@ package org.keycloak.proxy; import org.codehaus.jackson.annotate.JsonProperty; import org.keycloak.representations.adapters.config.AdapterConfig; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author Bill Burke @@ -41,6 +38,8 @@ public class ProxyConfig { protected boolean sendAccessToken; @JsonProperty("applications") protected List applications = new LinkedList(); + @JsonProperty("header-names") + private Map headerNames = new HashMap<>(); public String getBindAddress() { return bindAddress; @@ -154,6 +153,14 @@ public class ProxyConfig { this.sendAccessToken = sendAccessToken; } + public void setHeaderNames(Map headerNames) { + this.headerNames = headerNames; + } + + public Map getHeaderNames() { + return headerNames; + } + public static class Application { @JsonProperty("base-path") protected String basePath; @@ -212,6 +219,8 @@ public class ProxyConfig { protected boolean permit; @JsonProperty("authenticate") protected boolean authenticate; + @JsonProperty("inject-if-authenticated") + protected boolean injectIfAuthenticated; public String getPattern() { return pattern; @@ -253,6 +262,14 @@ public class ProxyConfig { this.authenticate = authenticate; } + public boolean isInjectIfAuthenticated() { + return injectIfAuthenticated; + } + + public void setInjectIfAuthenticated(boolean injectIfAuthenticated) { + this.injectIfAuthenticated = injectIfAuthenticated; + } + public Set getMethods() { return methods; } diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java index 1223faf245..7e9eb2de43 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java @@ -51,10 +51,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author Bill Burke @@ -76,6 +73,8 @@ public class ProxyServerBuilder { protected HttpHandler proxyHandler; protected boolean sendAccessToken; + protected Map headerNameConfig; + public ProxyServerBuilder target(String uri) { SimpleProxyClientProvider provider = null; try { @@ -98,6 +97,12 @@ public class ProxyServerBuilder { this.sendAccessToken = flag; return this; } + + public ProxyServerBuilder headerNameConfig(Map headerNameConfig) { + this.headerNameConfig = headerNameConfig; + return this; + } + public ApplicationBuilder application(AdapterConfig config) { return new ApplicationBuilder(config); } @@ -169,6 +174,11 @@ public class ProxyServerBuilder { return this; } + public ConstraintBuilder injectIfAuthenticated() { + semantic = SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED; + return this; + } + public ConstraintBuilder excludedMethods(Set excludedMethods) { this.excludedMethods = excludedMethods; return this; @@ -222,7 +232,7 @@ public class ProxyServerBuilder { errorPage = base + "/" + errorPage; } } - handler = new ConstraintAuthorizationHandler(handler, errorPage, sendAccessToken); + handler = new ConstraintAuthorizationHandler(handler, errorPage, sendAccessToken, headerNameConfig); handler = new ProxyAuthenticationCallHandler(handler); handler = new ConstraintMatcherHandler(matches, handler, toWrap, errorPage); final List mechanisms = new LinkedList(); @@ -373,6 +383,7 @@ public class ProxyServerBuilder { if (constraint.isDeny()) constraintBuilder.deny(); if (constraint.isPermit()) constraintBuilder.permit(); if (constraint.isAuthenticate()) constraintBuilder.authenticate(); + if (constraint.isInjectIfAuthenticated()) constraintBuilder.injectIfAuthenticated(); constraintBuilder.add(); } } @@ -383,6 +394,7 @@ public class ProxyServerBuilder { public static void initOptions(ProxyConfig config, ProxyServerBuilder builder) { builder.sendAccessToken(config.isSendAccessToken()); + builder.headerNameConfig(config.getHeaderNames()); if (config.getBufferSize() != null) builder.setBufferSize(config.getBufferSize()); if (config.getBuffersPerRegion() != null) builder.setBuffersPerRegion(config.getBuffersPerRegion()); if (config.getIoThreads() != null) builder.setIoThreads(config.getIoThreads()); diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java index 5d291373ab..a2eb42017f 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java @@ -46,8 +46,12 @@ public class SecurityInfo implements Cloneable { /** * Mandate authentication but authorize access as no roles to check against. */ - AUTHENTICATE; + AUTHENTICATE, + /** + * Permit access in any case, but provide authorization info only if authorized. + */ + INJECT_IF_AUTHENTICATED; } private volatile EmptyRoleSemantic emptyRoleSemantic = EmptyRoleSemantic.DENY; From a6eface8bc135c8686dee1b38fe23ff17d6954f3 Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Wed, 27 May 2015 15:01:49 +0200 Subject: [PATCH 2/6] configurable header names, documentation --- docbook/reference/en/en-US/modules/proxy.xml | 189 ++++++++++++------ .../proxy/ConstraintAuthorizationHandler.java | 2 +- .../proxy/ConstraintMatcherHandler.java | 3 +- .../java/org/keycloak/proxy/ProxyConfig.java | 12 +- .../keycloak/proxy/ProxyServerBuilder.java | 4 +- .../java/org/keycloak/proxy/SecurityInfo.java | 2 +- 6 files changed, 135 insertions(+), 77 deletions(-) diff --git a/docbook/reference/en/en-US/modules/proxy.xml b/docbook/reference/en/en-US/modules/proxy.xml index 3a17557dd2..20febf0c90 100755 --- a/docbook/reference/en/en-US/modules/proxy.xml +++ b/docbook/reference/en/en-US/modules/proxy.xml @@ -182,106 +182,162 @@ $ java -jar bin/launcher.jar [your-config.json]
Application Config - - Next under the applications array attribute, you can define one or more applications per host you are proxying. - - - base-path - - - The base context root for the application. Must start with '/' REQUIRED.. - - - - - error-page - - - If the proxy has an error, it will display the target application's error page relative URL OPTIONAL.. - This is a relative path to the base-path. In the example above it would be /customer-portal/error.html. - - - - - adapter-config - - - REQUIRED.. Same configuration as any other keycloak adapter. See Adapter Config - - - - - + + Next under the applications array attribute, you can define one or more applications per host you are proxying. + + + base-path + + + The base context root for the application. Must start with '/' REQUIRED.. + + + + + error-page + + + If the proxy has an error, it will display the target application's error page relative URL OPTIONAL.. + This is a relative path to the base-path. In the example above it would be /customer-portal/error.html. + + + + + adapter-config + + + REQUIRED.. Same configuration as any other keycloak adapter. See Adapter Config + + + + +
Constraint Config - - - Next under each application you can define one or more constraints in the constraints array attribute. - A constraint defines a URL pattern relative to the base-path. You can deny, permit, or require authentication for - a specific URL pattern. You can specify roles allowed for that path as well. More specific constraints will take - precedence over more general ones. + + Next under each application you can define one or more constraints in the constraints array attribute. + A constraint defines a URL pattern relative to the base-path. You can deny, permit, or require authentication for + a specific URL pattern. You can specify roles allowed for that path as well. More specific constraints will take + precedence over more general ones. + + + pattern + + + URL pattern to match relative to the base-path of the application. Must start with '/' REQUIRED.. + You may only have one wildcard and it must come at the end of the pattern. Valid /foo/bar/* and /foo/*.txt + Not valid: /*/foo/*. + + + + + roles-allowed + + + Array of strings of roles allowed to access this url pattern. OPTIONAL.. + + + + + methods + + + Array of strings of HTTP methods that will exclusively match this pattern and HTTP request. OPTIONAL.. + + + + + excluded-methods + + + Array of strings of HTTP methods that will be ignored when match this pattern. OPTIONAL.. + + + + + deny + + + Deny all access to this URL pattern. OPTIONAL.. + + + + + permit + + + Permit all access without requiring authentication or a role mapping. OPTIONAL.. + + + + + permit-and-inject + + + Permit all access, but inject the headers, if user is already authenticated.OPTIONAL.. + + + + + authenticate + + + Require authentication for this pattern, but no role mapping. OPTIONAL.. + + + + + +
+
+
+ Header Names Config + + Next under the list of applications you can override the defaults for the names of the header fields injected by the proxy (see Keycloak Identity Headers). + This mapping is optional. - pattern + keycloak-subject - URL pattern to match relative to the base-path of the application. Must start with '/' REQUIRED.. - You may only have one wildcard and it must come at the end of the pattern. Valid /foo/bar/* and /foo/*.txt - Not valid: /*/foo/*. + e.g. MYAPP_USER_ID - roles-allowed + kkeycloak-username - Array of strings of roles allowed to access this url pattern. OPTIONAL.. + e.g. MYAPP_USER_NAME - methods + keycloak-email - Array of strings of HTTP methods that will exclusively match this pattern and HTTP request. OPTIONAL.. + e.g. MYAPP_USER_EMAIL - excluded-methods + keycloak-name - Array of strings of HTTP methods that will be ignored when match this pattern. OPTIONAL.. + e.g. MYAPP_USER_ID - deny + keycloak-access-token - Deny all access to this URL pattern. OPTIONAL.. - - - - - permit - - - Permit all access without requiring authentication or a role mapping. OPTIONAL.. - - - - - authenticate - - - Require authentication for this pattern, but no role mapping. OPTIONAL.. + e.g. MYAPP_ACCESS_TOKEN - -
+
@@ -333,6 +389,7 @@ $ java -jar bin/launcher.jar [your-config.json] + Header field names can be configured using a map of header-names in configuration file.
\ No newline at end of file diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java index 5108149848..0bc23466d7 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java @@ -45,7 +45,7 @@ public class ConstraintAuthorizationHandler implements HttpHandler { } if (match != null) { - if(SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED.equals(match.getEmptyRoleSemantic())) { + if(SecurityInfo.EmptyRoleSemantic.PERMIT_AND_INJECT_IF_AUTHENTICATED.equals(match.getEmptyRoleSemantic())) { authenticatedRequest(account, exchange); return; } else { diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java index f1347bb50d..31a187cf5e 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintMatcherHandler.java @@ -49,7 +49,8 @@ public class ConstraintMatcherHandler implements HttpHandler { return; } - if (match.getRequiredRoles().isEmpty() && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED) { + if (match.getRequiredRoles().isEmpty() + && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.PERMIT_AND_INJECT_IF_AUTHENTICATED) { boolean successfulAuthenticatedMethodFound = isSuccessfulAuthenticatedMethodFound(exchange); diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java index e0cdbab763..b211fdeb61 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java @@ -219,8 +219,8 @@ public class ProxyConfig { protected boolean permit; @JsonProperty("authenticate") protected boolean authenticate; - @JsonProperty("inject-if-authenticated") - protected boolean injectIfAuthenticated; + @JsonProperty("permit-and-inject") + protected boolean permitAndInject; public String getPattern() { return pattern; @@ -262,12 +262,12 @@ public class ProxyConfig { this.authenticate = authenticate; } - public boolean isInjectIfAuthenticated() { - return injectIfAuthenticated; + public boolean isPermitAndInject() { + return permitAndInject; } - public void setInjectIfAuthenticated(boolean injectIfAuthenticated) { - this.injectIfAuthenticated = injectIfAuthenticated; + public void setPermitAndInject(boolean permitAndInject) { + this.permitAndInject = permitAndInject; } public Set getMethods() { diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java index 7e9eb2de43..d689e594cf 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyServerBuilder.java @@ -175,7 +175,7 @@ public class ProxyServerBuilder { } public ConstraintBuilder injectIfAuthenticated() { - semantic = SecurityInfo.EmptyRoleSemantic.INJECT_IF_AUTHENTICATED; + semantic = SecurityInfo.EmptyRoleSemantic.PERMIT_AND_INJECT_IF_AUTHENTICATED; return this; } @@ -383,7 +383,7 @@ public class ProxyServerBuilder { if (constraint.isDeny()) constraintBuilder.deny(); if (constraint.isPermit()) constraintBuilder.permit(); if (constraint.isAuthenticate()) constraintBuilder.authenticate(); - if (constraint.isInjectIfAuthenticated()) constraintBuilder.injectIfAuthenticated(); + if (constraint.isPermitAndInject()) constraintBuilder.injectIfAuthenticated(); constraintBuilder.add(); } } diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java index a2eb42017f..f7f98af228 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/SecurityInfo.java @@ -51,7 +51,7 @@ public class SecurityInfo implements Cloneable { /** * Permit access in any case, but provide authorization info only if authorized. */ - INJECT_IF_AUTHENTICATED; + PERMIT_AND_INJECT_IF_AUTHENTICATED; } private volatile EmptyRoleSemantic emptyRoleSemantic = EmptyRoleSemantic.DENY; From 2b5e0e238dce81c2eb09a7d78ffb956f29f9c726 Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Thu, 28 May 2015 18:30:18 +0200 Subject: [PATCH 3/6] constants --- .../proxy/ConstraintAuthorizationHandler.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java index 0bc23466d7..199bded0c2 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java @@ -15,7 +15,13 @@ import java.util.Map; */ public class ConstraintAuthorizationHandler implements HttpHandler { + public static final String KEYCLOAK_SUBJECT = "KEYCLOAK_SUBJECT"; + public static final String KEYCLOAK_USERNAME = "KEYCLOAK_USERNAME"; + public static final String KEYCLOAK_EMAIL = "KEYCLOAK_EMAIL"; + public static final String KEYCLOAK_NAME = "KEYCLOAK_NAME"; + public static final String KEYCLOAK_ACCESS_TOKEN = "KEYCLOAK_ACCESS_TOKEN"; private final Map httpHeaderNames; + protected HttpHandler next; protected String errorPage; protected boolean sendAccessToken; @@ -26,11 +32,11 @@ public class ConstraintAuthorizationHandler implements HttpHandler { this.sendAccessToken = sendAccessToken; this.httpHeaderNames = new HashMap<>(); - this.httpHeaderNames.put("KEYCLOAK_SUBJECT", new HttpString(headerNames.getOrDefault("keycloak-subject", "KEYCLOAK_SUBJECT"))); - this.httpHeaderNames.put("KEYCLOAK_USERNAME", new HttpString(headerNames.getOrDefault("keycloak-username", "KEYCLOAK_USERNAME"))); - this.httpHeaderNames.put("KEYCLOAK_EMAIL", new HttpString(headerNames.getOrDefault("keycloak-email", "KEYCLOAK_EMAIL"))); - this.httpHeaderNames.put("KEYCLOAK_NAME", new HttpString(headerNames.getOrDefault("keycloak-name", "KEYCLOAK_NAME"))); - this.httpHeaderNames.put("KEYCLOAK_ACCESS_TOKEN", new HttpString(headerNames.getOrDefault("keycloak-access-token", "KEYCLOAK_ACCESS_TOKEN"))); + this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(headerNames.getOrDefault("keycloak-subject", KEYCLOAK_SUBJECT))); + this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(headerNames.getOrDefault("keycloak-username", KEYCLOAK_USERNAME))); + this.httpHeaderNames.put(KEYCLOAK_EMAIL, new HttpString(headerNames.getOrDefault("keycloak-email", KEYCLOAK_EMAIL))); + this.httpHeaderNames.put(KEYCLOAK_NAME, new HttpString(headerNames.getOrDefault("keycloak-name", KEYCLOAK_NAME))); + this.httpHeaderNames.put(KEYCLOAK_ACCESS_TOKEN, new HttpString(headerNames.getOrDefault("keycloak-access-token", KEYCLOAK_ACCESS_TOKEN))); } @Override @@ -76,20 +82,20 @@ public class ConstraintAuthorizationHandler implements HttpHandler { IDToken idToken = account.getKeycloakSecurityContext().getToken(); if (idToken == null) return; if (idToken.getSubject() != null) { - exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_SUBJECT"), idToken.getSubject()); + exchange.getRequestHeaders().put(httpHeaderNames.get(KEYCLOAK_SUBJECT), idToken.getSubject()); } if (idToken.getPreferredUsername() != null) { - exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_USERNAME"), idToken.getPreferredUsername()); + exchange.getRequestHeaders().put(httpHeaderNames.get(KEYCLOAK_USERNAME), idToken.getPreferredUsername()); } if (idToken.getEmail() != null) { - exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_EMAIL"), idToken.getEmail()); + exchange.getRequestHeaders().put(httpHeaderNames.get(KEYCLOAK_EMAIL), idToken.getEmail()); } if (idToken.getName() != null) { - exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_NAME"), idToken.getName()); + exchange.getRequestHeaders().put(httpHeaderNames.get(KEYCLOAK_NAME), idToken.getName()); } if (sendAccessToken) { - exchange.getRequestHeaders().put(httpHeaderNames.get("KEYCLOAK_ACCESS_TOKEN"), account.getKeycloakSecurityContext().getTokenString()); + exchange.getRequestHeaders().put(httpHeaderNames.get(KEYCLOAK_ACCESS_TOKEN), account.getKeycloakSecurityContext().getTokenString()); } } next.handleRequest(exchange); From 136f2c8bb706ca42eaedb8bea74f5ba5a548451b Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Fri, 29 May 2015 07:57:56 +0200 Subject: [PATCH 4/6] documentation fixed --- docbook/reference/en/en-US/modules/proxy.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docbook/reference/en/en-US/modules/proxy.xml b/docbook/reference/en/en-US/modules/proxy.xml index 20febf0c90..e707f8d2ed 100755 --- a/docbook/reference/en/en-US/modules/proxy.xml +++ b/docbook/reference/en/en-US/modules/proxy.xml @@ -389,7 +389,14 @@ $ java -jar bin/launcher.jar [your-config.json] - Header field names can be configured using a map of header-names in configuration file. + Header field names can be configured using a map of header-names in configuration file: + \ No newline at end of file From e8c90c13275fefdcc9980279f92f9e47e0531849 Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Fri, 29 May 2015 13:34:00 +0200 Subject: [PATCH 5/6] fixed typo in doc --- docbook/reference/en/en-US/modules/proxy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docbook/reference/en/en-US/modules/proxy.xml b/docbook/reference/en/en-US/modules/proxy.xml index e707f8d2ed..b38fbbb646 100755 --- a/docbook/reference/en/en-US/modules/proxy.xml +++ b/docbook/reference/en/en-US/modules/proxy.xml @@ -305,7 +305,7 @@ $ java -jar bin/launcher.jar [your-config.json] - kkeycloak-username + keycloak-username e.g. MYAPP_USER_NAME From b4204fc8062689d552ce2e7bffd51e6152a30a20 Mon Sep 17 00:00:00 2001 From: Sebastian Rose Date: Fri, 29 May 2015 13:45:09 +0200 Subject: [PATCH 6/6] getOrDefault not in java 7 --- .../proxy/ConstraintAuthorizationHandler.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java index 199bded0c2..365feea9a6 100755 --- a/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java +++ b/proxy/proxy-server/src/main/java/org/keycloak/proxy/ConstraintAuthorizationHandler.java @@ -32,11 +32,15 @@ public class ConstraintAuthorizationHandler implements HttpHandler { this.sendAccessToken = sendAccessToken; this.httpHeaderNames = new HashMap<>(); - this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(headerNames.getOrDefault("keycloak-subject", KEYCLOAK_SUBJECT))); - this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(headerNames.getOrDefault("keycloak-username", KEYCLOAK_USERNAME))); - this.httpHeaderNames.put(KEYCLOAK_EMAIL, new HttpString(headerNames.getOrDefault("keycloak-email", KEYCLOAK_EMAIL))); - this.httpHeaderNames.put(KEYCLOAK_NAME, new HttpString(headerNames.getOrDefault("keycloak-name", KEYCLOAK_NAME))); - this.httpHeaderNames.put(KEYCLOAK_ACCESS_TOKEN, new HttpString(headerNames.getOrDefault("keycloak-access-token", KEYCLOAK_ACCESS_TOKEN))); + this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(getOrDefault(headerNames, "keycloak-subject", KEYCLOAK_SUBJECT))); + this.httpHeaderNames.put(KEYCLOAK_SUBJECT, new HttpString(getOrDefault(headerNames, "keycloak-username", KEYCLOAK_USERNAME))); + this.httpHeaderNames.put(KEYCLOAK_EMAIL, new HttpString(getOrDefault(headerNames, "keycloak-email", KEYCLOAK_EMAIL))); + this.httpHeaderNames.put(KEYCLOAK_NAME, new HttpString(getOrDefault(headerNames, "keycloak-name", KEYCLOAK_NAME))); + this.httpHeaderNames.put(KEYCLOAK_ACCESS_TOKEN, new HttpString(getOrDefault(headerNames, "keycloak-access-token", KEYCLOAK_ACCESS_TOKEN))); + } + + private String getOrDefault(Map map, String key, String defaultValue) { + return map.containsKey(key) ? map.get(key) : defaultValue; } @Override