keycloak-aplcache

Merge pull request #1298 from AOEpeople/KEYCLOAK-1103 KEYCLOAK-1103

6/2/2015 9:04:05 AM

Details

diff --git a/docbook/reference/en/en-US/modules/proxy.xml b/docbook/reference/en/en-US/modules/proxy.xml
index 3a17557..b38fbbb 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]
         </section>
         <section>
             <title>Application Config</title>
-        <para>
-            Next under the <literal>applications</literal> array attribute, you can define one or more applications per host you are proxying.
-            <variablelist>
-                <varlistentry>
-                    <term>base-path</term>
-                    <listitem>
-                        <para>
-                            The base context root for the application.  Must start with '/' <emphasis>REQUIRED.</emphasis>.
-                        </para>
-                    </listitem>
-                </varlistentry>
-                <varlistentry>
-                    <term>error-page</term>
-                    <listitem>
-                        <para>
-                            If the proxy has an error, it will display the target application's error page relative URL <emphasis>OPTIONAL.</emphasis>.
-                            This is a relative path to the base-path.  In the example above it would be <literal>/customer-portal/error.html</literal>.
-                        </para>
-                    </listitem>
-                </varlistentry>
-                <varlistentry>
-                    <term>adapter-config</term>
-                    <listitem>
-                        <para>
-                            <emphasis>REQUIRED.</emphasis>.  Same configuration as any other keycloak adapter.  See <link linkend='adapter-config'>Adapter Config</link>
-                        </para>
-                    </listitem>
-                </varlistentry>
-            </variablelist>
-        </para>
+            <para>
+                Next under the <literal>applications</literal> array attribute, you can define one or more applications per host you are proxying.
+                <variablelist>
+                    <varlistentry>
+                        <term>base-path</term>
+                        <listitem>
+                            <para>
+                                The base context root for the application.  Must start with '/' <emphasis>REQUIRED.</emphasis>.
+                            </para>
+                        </listitem>
+                    </varlistentry>
+                    <varlistentry>
+                        <term>error-page</term>
+                        <listitem>
+                            <para>
+                                If the proxy has an error, it will display the target application's error page relative URL <emphasis>OPTIONAL.</emphasis>.
+                                This is a relative path to the base-path.  In the example above it would be <literal>/customer-portal/error.html</literal>.
+                            </para>
+                        </listitem>
+                    </varlistentry>
+                    <varlistentry>
+                        <term>adapter-config</term>
+                        <listitem>
+                            <para>
+                                <emphasis>REQUIRED.</emphasis>.  Same configuration as any other keycloak adapter.  See <link linkend='adapter-config'>Adapter Config</link>
+                            </para>
+                        </listitem>
+                    </varlistentry>
+                </variablelist>
+            </para>
             <section>
                 <title>Constraint Config</title>
-
-        <para>
-            Next under each application you can define one or more constraints in the <literal>constraints</literal> 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.
+                <para>
+                    Next under each application you can define one or more constraints in the <literal>constraints</literal> 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.
+                    <variablelist>
+                        <varlistentry>
+                            <term>pattern</term>
+                            <listitem>
+                                <para>
+                                    URL pattern to match relative to the base-path of the application.  Must start with '/' <emphasis>REQUIRED.</emphasis>.
+                                    You may only have one wildcard and it must come at the end of the pattern.  Valid <literal>/foo/bar/*</literal> and  <literal>/foo/*.txt</literal>
+                                    Not valid: <literal>/*/foo/*</literal>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>roles-allowed</term>
+                            <listitem>
+                                <para>
+                                    Array of strings of roles allowed to access this url pattern. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>methods</term>
+                            <listitem>
+                                <para>
+                                    Array of strings of HTTP methods that will exclusively match this pattern and HTTP request. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>excluded-methods</term>
+                            <listitem>
+                                <para>
+                                    Array of strings of HTTP methods that will be ignored when match this pattern. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>deny</term>
+                            <listitem>
+                                <para>
+                                    Deny all access to this URL pattern. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>permit</term>
+                            <listitem>
+                                <para>
+                                    Permit all access without requiring authentication or a role mapping. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>permit-and-inject</term>
+                            <listitem>
+                                <para>
+                                    Permit all access, but inject the headers, if user is already authenticated.<emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                        <varlistentry>
+                            <term>authenticate</term>
+                            <listitem>
+                                <para>
+                                    Require authentication for this pattern, but no role mapping. <emphasis>OPTIONAL.</emphasis>.
+                                </para>
+                            </listitem>
+                        </varlistentry>
+                    </variablelist>
+                </para>
+            </section>
+        </section>
+        <section>
+            <title>Header Names Config</title>
+            <para>
+            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.
             <variablelist>
                 <varlistentry>
-                    <term>pattern</term>
-                    <listitem>
-                        <para>
-                            URL pattern to match relative to the base-path of the application.  Must start with '/' <emphasis>REQUIRED.</emphasis>.
-                            You may only have one wildcard and it must come at the end of the pattern.  Valid <literal>/foo/bar/*</literal> and  <literal>/foo/*.txt</literal>
-                            Not valid: <literal>/*/foo/*</literal>.
-                        </para>
-                    </listitem>
-                </varlistentry>
-                <varlistentry>
-                    <term>roles-allowed</term>
-                    <listitem>
-                        <para>
-                            Array of strings of roles allowed to access this url pattern. <emphasis>OPTIONAL.</emphasis>.
-                        </para>
-                    </listitem>
-                </varlistentry>
-                <varlistentry>
-                    <term>methods</term>
+                    <term>keycloak-subject</term>
                     <listitem>
                         <para>
-                            Array of strings of HTTP methods that will exclusively match this pattern and HTTP request. <emphasis>OPTIONAL.</emphasis>.
+                            e.g. MYAPP_USER_ID
                         </para>
                     </listitem>
                 </varlistentry>
                 <varlistentry>
-                    <term>excluded-methods</term>
+                    <term>keycloak-username</term>
                     <listitem>
                         <para>
-                            Array of strings of HTTP methods that will be ignored when match this pattern. <emphasis>OPTIONAL.</emphasis>.
+                            e.g. MYAPP_USER_NAME
                         </para>
                     </listitem>
                 </varlistentry>
                 <varlistentry>
-                    <term>deny</term>
+                    <term>keycloak-email</term>
                     <listitem>
                         <para>
-                            Deny all access to this URL pattern. <emphasis>OPTIONAL.</emphasis>.
+                            e.g. MYAPP_USER_EMAIL
                         </para>
                     </listitem>
                 </varlistentry>
                 <varlistentry>
-                    <term>permit</term>
+                    <term>keycloak-name</term>
                     <listitem>
                         <para>
-                            Permit all access without requiring authentication or a role mapping. <emphasis>OPTIONAL.</emphasis>.
+                            e.g. MYAPP_USER_ID
                         </para>
                     </listitem>
                 </varlistentry>
                 <varlistentry>
-                    <term>authenticate</term>
+                    <term>keycloak-access-token</term>
                     <listitem>
                         <para>
-                            Require authentication for this pattern, but no role mapping. <emphasis>OPTIONAL.</emphasis>.
+                            e.g. MYAPP_ACCESS_TOKEN
                         </para>
                     </listitem>
                 </varlistentry>
             </variablelist>
-        </para>
-        </section>
+            </para>
         </section>
     </section>
     <section>
@@ -333,6 +389,14 @@ $ java -jar bin/launcher.jar [your-config.json]
                     </listitem>
                 </varlistentry>
             </variablelist>
+            Header field names can be configured using a map of <literal>header-names</literal> in configuration file:
+            <programlisting><![CDATA[
+{
+    "header-names" {
+        "keycloak-subject": "MY_SUBJECT"
+    }
+}
+            ]]></programlisting>
         </para>
     </section>
 </chapter>
\ 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 58186dc..365feea 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,68 @@ 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @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");
+
+    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<String, HttpString> 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<String, String> headerNames) {
         this.next = next;
         this.errorPage = errorPage;
         this.sendAccessToken = sendAccessToken;
+
+        this.httpHeaderNames = new HashMap<>();
+        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<String, String> map, String key, String defaultValue) {
+        return map.containsKey(key) ? map.get(key) : defaultValue;
     }
 
     @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.PERMIT_AND_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 +86,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 998302d..31a187c 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -47,10 +48,42 @@ public class ConstraintMatcherHandler implements HttpHandler {
             }
             return;
         }
+
+        if (match.getRequiredRoles().isEmpty()
+                && match.getEmptyRoleSemantic() == SecurityInfo.EmptyRoleSemantic.PERMIT_AND_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<AuthenticationMechanism> 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 dd41a05..b211fde 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -41,6 +38,8 @@ public class ProxyConfig {
     protected boolean sendAccessToken;
     @JsonProperty("applications")
     protected List<Application> applications = new LinkedList<Application>();
+    @JsonProperty("header-names")
+    private Map<String,String> headerNames = new HashMap<>();
 
     public String getBindAddress() {
         return bindAddress;
@@ -154,6 +153,14 @@ public class ProxyConfig {
         this.sendAccessToken = sendAccessToken;
     }
 
+    public void setHeaderNames(Map<String, String> headerNames) {
+        this.headerNames = headerNames;
+    }
+
+    public Map<String, String> 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("permit-and-inject")
+        protected boolean permitAndInject;
 
         public String getPattern() {
             return pattern;
@@ -253,6 +262,14 @@ public class ProxyConfig {
             this.authenticate = authenticate;
         }
 
+        public boolean isPermitAndInject() {
+            return permitAndInject;
+        }
+
+        public void setPermitAndInject(boolean permitAndInject) {
+            this.permitAndInject = permitAndInject;
+        }
+
         public Set<String> 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 1223faf..d689e59 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -76,6 +73,8 @@ public class ProxyServerBuilder {
     protected HttpHandler proxyHandler;
     protected boolean sendAccessToken;
 
+    protected Map<String, String> 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<String, String> 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.PERMIT_AND_INJECT_IF_AUTHENTICATED;
+                return this;
+            }
+
             public ConstraintBuilder excludedMethods(Set<String> 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<AuthenticationMechanism> mechanisms = new LinkedList<AuthenticationMechanism>();
@@ -373,6 +383,7 @@ public class ProxyServerBuilder {
                     if (constraint.isDeny()) constraintBuilder.deny();
                     if (constraint.isPermit()) constraintBuilder.permit();
                     if (constraint.isAuthenticate()) constraintBuilder.authenticate();
+                    if (constraint.isPermitAndInject()) 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 5d29137..f7f98af 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<T extends 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.
+         */
+        PERMIT_AND_INJECT_IF_AUTHENTICATED;
     }
 
     private volatile EmptyRoleSemantic emptyRoleSemantic = EmptyRoleSemantic.DENY;