-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
500 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<classpath> | ||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
<attribute name="optional" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> | ||
<attributes> | ||
<attribute name="test" value="true"/> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> | ||
<attributes> | ||
<attribute name="test" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
<attribute name="optional" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" output="target/classes" path="src/main/java"> | ||
<attributes> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="output" path="target/classes"/> | ||
</classpath> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/bin/ | ||
/target/ | ||
deployments/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<projectDescription> | ||
<name>keycloak-conditional-http-header-authenticator</name> | ||
<comment></comment> | ||
<projects> | ||
</projects> | ||
<buildSpec> | ||
<buildCommand> | ||
<name>org.eclipse.jdt.core.javabuilder</name> | ||
<arguments> | ||
</arguments> | ||
</buildCommand> | ||
<buildCommand> | ||
<name>org.eclipse.m2e.core.maven2Builder</name> | ||
<arguments> | ||
</arguments> | ||
</buildCommand> | ||
</buildSpec> | ||
<natures> | ||
<nature>org.eclipse.jdt.core.javanature</nature> | ||
<nature>org.eclipse.m2e.core.maven2Nature</nature> | ||
</natures> | ||
</projectDescription> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
eclipse.preferences.version=1 | ||
encoding//src/main/java=UTF-8 | ||
encoding//src/main/resources=UTF-8 | ||
encoding//src/test/java=UTF-8 | ||
encoding//src/test/resources=UTF-8 | ||
encoding/<project>=UTF-8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
eclipse.preferences.version=1 | ||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 | ||
org.eclipse.jdt.core.compiler.compliance=17 | ||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled | ||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning | ||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore | ||
org.eclipse.jdt.core.compiler.release=disabled | ||
org.eclipse.jdt.core.compiler.source=17 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,21 @@ | ||
# keycloak-conditional-http-header-authenticator | ||
Keycloak Authenticator matching HTTP header | ||
# Keycloak Conditional HTTP Header Authenticator | ||
|
||
Check the http request header to match on a regex string. | ||
|
||
Initial code by Sebastian Preisner, copied from https://github.com/keycloak/keycloak/pull/14605. | ||
|
||
Relevant Keycloak Documentation part: https://www.keycloak.org/docs/latest/server_admin/#conditions-in-conditional-flows | ||
|
||
### Build | ||
|
||
Use with Maven (>3) and JDK (>= 17). | ||
|
||
``` | ||
mvn clean verify | ||
``` | ||
|
||
Copy deployments/conditional-http-header-authenticator-jar-with-dependencies.jar to /providers/ directory in Keycloak. | ||
|
||
### Example | ||
|
||
See https://github.com/elexis/keycloak-auth-deny-user-nosecondfactor for a usage example. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
|
||
<name>Conditional HTTP Header Check</name> | ||
<description/> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>info.elexis</groupId> | ||
<artifactId>keycloak-conditional-http-header-authenticator</artifactId> | ||
<version>0.1.0</version> | ||
<packaging>jar</packaging> | ||
|
||
<properties> | ||
<java.version>17</java.version> | ||
<keycloak.version>24.0.5</keycloak.version> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<maven.compiler.source>${java.version}</maven.compiler.source> | ||
<maven.compiler.target>${java.version}</maven.compiler.target> | ||
<junit.version>4.13.2</junit.version> | ||
<jboss.logging.version>3.4.1.Final</jboss.logging.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-core</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-server-spi</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-server-spi-private</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.jboss.logging</groupId> | ||
<artifactId>jboss-logging</artifactId> | ||
<version>${jboss.logging.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-services</artifactId> | ||
<version>${keycloak.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<version>${junit.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
|
||
<build> | ||
<finalName>conditional-http-header-authenticator</finalName> | ||
<plugins> | ||
<plugin> | ||
<artifactId>maven-assembly-plugin</artifactId> | ||
<version>3.3.0</version> | ||
<configuration> | ||
<descriptorRefs> | ||
<descriptorRef>jar-with-dependencies</descriptorRef> | ||
</descriptorRefs> | ||
<outputDirectory>deployments</outputDirectory> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<id>make-assembly</id> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>single</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> | ||
|
79 changes: 79 additions & 0 deletions
79
.../info/elexis/conditionalhttpheader/authentication/ConditionalHttpHeaderAuthenticator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package info.elexis.conditionalhttpheader.authentication; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.regex.Pattern; | ||
|
||
import org.jboss.logging.Logger; | ||
import org.keycloak.authentication.AuthenticationFlowContext; | ||
import org.keycloak.authentication.authenticators.conditional.ConditionalAuthenticator; | ||
import org.keycloak.models.KeycloakSession; | ||
import org.keycloak.models.RealmModel; | ||
import org.keycloak.models.UserModel; | ||
|
||
import jakarta.ws.rs.core.MultivaluedMap; | ||
|
||
public class ConditionalHttpHeaderAuthenticator implements ConditionalAuthenticator { | ||
|
||
private static final Logger logger = Logger.getLogger(ConditionalHttpHeaderAuthenticator.class); | ||
|
||
public static final ConditionalHttpHeaderAuthenticator SINGLETON = new ConditionalHttpHeaderAuthenticator(); | ||
|
||
public boolean containsMatchingRequestHeader(MultivaluedMap<String, String> requestHeaders, String headerPattern) { | ||
if (headerPattern == null) { | ||
logger.debugv("The matching request header patterns are <null>!"); | ||
return false; | ||
} | ||
|
||
Pattern pattern = Pattern.compile(headerPattern, Pattern.DOTALL | Pattern.CASE_INSENSITIVE); | ||
|
||
for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) { | ||
|
||
String key = entry.getKey(); | ||
for (String value : entry.getValue()) { | ||
String headerEntry = key.trim() + ": " + value.trim(); | ||
if (pattern.matcher(headerEntry).matches()) { | ||
logger.debugv("Pattern {0} matches header entry {1}", headerPattern, headerEntry); | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public boolean matchCondition(AuthenticationFlowContext context) { | ||
Map<String, String> config = context.getAuthenticatorConfig().getConfig(); | ||
if (config == null) { | ||
return false; | ||
} | ||
|
||
MultivaluedMap<String, String> requestHeaders = context.getHttpRequest().getHttpHeaders().getRequestHeaders(); | ||
String headerPattern = config.get(ConditionalHttpHeaderAuthenticatorFactory.HTTP_HEADER_PATTERN); | ||
boolean negateOutcome = Boolean | ||
.parseBoolean(config.get(ConditionalHttpHeaderAuthenticatorFactory.NEGATE_OUTCOME)); | ||
|
||
return (negateOutcome != containsMatchingRequestHeader(requestHeaders, headerPattern)); | ||
} | ||
|
||
@Override | ||
public boolean requiresUser() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public void action(AuthenticationFlowContext context) { | ||
// Not used | ||
} | ||
|
||
@Override | ||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { | ||
// Not used | ||
} | ||
|
||
@Override | ||
public void close() { | ||
// Does nothing | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
...lexis/conditionalhttpheader/authentication/ConditionalHttpHeaderAuthenticatorFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package info.elexis.conditionalhttpheader.authentication; | ||
|
||
import org.keycloak.Config.Scope; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import org.keycloak.authentication.authenticators.conditional.ConditionalAuthenticator; | ||
import org.keycloak.authentication.authenticators.conditional.ConditionalAuthenticatorFactory; | ||
import org.keycloak.models.AuthenticationExecutionModel; | ||
import org.keycloak.models.AuthenticationExecutionModel.Requirement; | ||
import org.keycloak.models.KeycloakSessionFactory; | ||
import org.keycloak.provider.ProviderConfigProperty; | ||
|
||
/** | ||
* An {@link ConditionalAuthenticatorFactory} for | ||
* {@link ConditionalHttpHeaderAuthenticator}s. | ||
* | ||
* @author <a href="mailto:preisner@puzzle-itc.de">Sebastian Preisner</a> | ||
*/ | ||
public class ConditionalHttpHeaderAuthenticatorFactory implements ConditionalAuthenticatorFactory { | ||
public static final String PROVIDER_ID = "conditional-http-header"; | ||
|
||
public static final String HTTP_HEADER_PATTERN = "search_pattern"; | ||
public static final String NEGATE_OUTCOME = "negate_outcome"; | ||
|
||
@Override | ||
public void init(Scope config) { | ||
// no-op | ||
} | ||
|
||
@Override | ||
public void postInit(KeycloakSessionFactory factory) { | ||
// no-op | ||
} | ||
|
||
@Override | ||
public void close() { | ||
// no-op | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return PROVIDER_ID; | ||
} | ||
|
||
@Override | ||
public String getDisplayType() { | ||
return "Condition - request header"; | ||
} | ||
|
||
@Override | ||
public boolean isConfigurable() { | ||
return true; | ||
} | ||
|
||
private static final Requirement[] REQUIREMENT_CHOICES = { AuthenticationExecutionModel.Requirement.REQUIRED, | ||
AuthenticationExecutionModel.Requirement.DISABLED }; | ||
|
||
@Override | ||
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { | ||
return REQUIREMENT_CHOICES; | ||
} | ||
|
||
@Override | ||
public boolean isUserSetupAllowed() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public String getHelpText() { | ||
return "Flow is executed olny if HTTP request header matches supplied regular expression."; | ||
} | ||
|
||
@Override | ||
public List<ProviderConfigProperty> getConfigProperties() { | ||
|
||
ProviderConfigProperty HttpHeaderPattern = new ProviderConfigProperty(); | ||
HttpHeaderPattern.setType(ProviderConfigProperty.STRING_TYPE); | ||
HttpHeaderPattern.setName(HTTP_HEADER_PATTERN); | ||
HttpHeaderPattern.setLabel("HTTP Header Pattern"); | ||
HttpHeaderPattern.setHelpText("If a HTTP request header matches the given pattern the condition will be true." | ||
+ "Can be used to specify trusted networks via: X-Forwarded-Host: (1.2.3.4|1.2.3.5)." | ||
+ "In this case requests from 1.2.3.4 and 1.2.3.5 come from a trusted source."); | ||
HttpHeaderPattern.setDefaultValue(""); | ||
|
||
ProviderConfigProperty negateOutcome = new ProviderConfigProperty(); | ||
negateOutcome.setType(ProviderConfigProperty.BOOLEAN_TYPE); | ||
negateOutcome.setName(NEGATE_OUTCOME); | ||
negateOutcome.setLabel("Negate"); | ||
negateOutcome.setHelpText("Send false if the given pattern matches."); | ||
|
||
return Arrays.asList(HttpHeaderPattern, negateOutcome); | ||
} | ||
|
||
@Override | ||
public ConditionalAuthenticator getSingleton() { | ||
return ConditionalHttpHeaderAuthenticator.SINGLETON; | ||
} | ||
|
||
} |
1 change: 1 addition & 0 deletions
1
src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
info.elexis.conditionalhttpheader.authentication.ConditionalHttpHeaderAuthenticatorFactory |
Oops, something went wrong.