keycloak-aplcache
Changes
distribution/modules/build.xml 3(+0 -3)
distribution/modules/pom.xml 5(+0 -5)
distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml 1(+1 -0)
examples/basic-auth/basicauthrealm.json 56(+56 -0)
examples/basic-auth/pom.xml 94(+94 -0)
examples/basic-auth/README.md 29(+29 -0)
examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthServiceApplication.java 12(+12 -0)
examples/pom.xml 1(+1 -0)
integration/adapter-core/pom.xml 5(+5 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java 10(+10 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java 112(+112 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java 4(+4 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java 1(+1 -0)
integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java 4(+2 -2)
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java 15(+11 -4)
integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java 2(+1 -1)
integration/keycloak-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java 7(+7 -0)
integration/keycloak-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties 1(+1 -0)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java 4(+2 -2)
integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java 5(+3 -2)
Details
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
index 58d49ea..676a103 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
@@ -16,7 +16,7 @@ import java.util.Map;
"resource", "public-client", "credentials",
"use-resource-role-mappings",
"enable-cors", "cors-max-age", "cors-allowed-methods",
- "expose-token", "bearer-only"})
+ "expose-token", "bearer-only", "enable-basic-auth"})
public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("resource")
protected String resource;
@@ -34,6 +34,8 @@ public class BaseAdapterConfig extends BaseRealmConfig {
protected boolean exposeToken;
@JsonProperty("bearer-only")
protected boolean bearerOnly;
+ @JsonProperty("enable-basic-auth")
+ protected boolean enableBasicAuth;
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
@@ -97,12 +99,20 @@ public class BaseAdapterConfig extends BaseRealmConfig {
}
public boolean isBearerOnly() {
- return bearerOnly;
- }
+ return bearerOnly;
+ }
public void setBearerOnly(boolean bearerOnly) {
- this.bearerOnly = bearerOnly;
- }
+ this.bearerOnly = bearerOnly;
+ }
+
+ public boolean isEnableBasicAuth() {
+ return enableBasicAuth;
+ }
+
+ public void setEnableBasicAuth(boolean enableBasicAuth) {
+ this.enableBasicAuth = enableBasicAuth;
+ }
public Map<String, String> getCredentials() {
return credentials;
diff --git a/distribution/examples-docs-zip/build.xml b/distribution/examples-docs-zip/build.xml
index 16f93f5..503dc8e 100755
--- a/distribution/examples-docs-zip/build.xml
+++ b/distribution/examples-docs-zip/build.xml
@@ -50,6 +50,14 @@
<exclude name="**/subsystem-config.xml"/>
</fileset>
</copy>
+ <copy todir="target/examples/basic-auth" overwrite="true">
+ <fileset dir="../../examples/basic-auth">
+ <exclude name="**/target/**"/>
+ <exclude name="**/*.iml"/>
+ <exclude name="**/*.unconfigured"/>
+ <exclude name="**/subsystem-config.xml"/>
+ </fileset>
+ </copy>
<copy todir="target/examples/admin-client" overwrite="true">
<fileset dir="../../examples/admin-client">
<exclude name="**/target/**"/>
distribution/modules/build.xml 3(+0 -3)
diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml
index 14ed2cd..a9da469 100755
--- a/distribution/modules/build.xml
+++ b/distribution/modules/build.xml
@@ -86,9 +86,6 @@
<module-def name="org.keycloak.keycloak-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-subsystem"/>
</module-def>
- <module-def name="org.keycloak.keycloak-as7-subsystem">
- <maven-resource group="org.keycloak" artifact="keycloak-as7-subsystem"/>
- </module-def>
<module-def name="org.picketlink">
</module-def>
<module-def name="org.picketlink.common">
distribution/modules/pom.xml 5(+0 -5)
diff --git a/distribution/modules/pom.xml b/distribution/modules/pom.xml
index 5a286ab..f3a2a08 100755
--- a/distribution/modules/pom.xml
+++ b/distribution/modules/pom.xml
@@ -53,11 +53,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-as7-subsystem</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
</dependency>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
index 5323ea3..2af6613 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -34,6 +34,7 @@
<module name="org.apache.httpcomponents" />
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core"/>
+ <module name="net.iharder.base64"/>
</dependencies>
</module>
diff --git a/docbook/reference/en/en-US/modules/adapter-config.xml b/docbook/reference/en/en-US/modules/adapter-config.xml
index 6eddb77..979a9d5 100755
--- a/docbook/reference/en/en-US/modules/adapter-config.xml
+++ b/docbook/reference/en/en-US/modules/adapter-config.xml
@@ -16,6 +16,7 @@
"cors-max-age" : 1000,
"cors-allowed-methods" : [ "POST", "PUT", "DELETE", "GET" ],
"bearer-only" : false,
+ "enable-basic-auth" : false,
"expose-token" : true,
"credentials" : {
"secret" : "234234-234234-234234"
@@ -158,6 +159,16 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term>enable-basic-auth</term>
+ <listitem>
+ <para>
+ This tells the adapter to also support basic authentication. If this option is enabled,
+ then <emphasis>secret</emphasis> must also be provided.
+ This is <emphasis>OPTIONAL</emphasis>. The default value is <emphasis>false</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term>expose-token</term>
<listitem>
<para>
examples/basic-auth/basicauthrealm.json 56(+56 -0)
diff --git a/examples/basic-auth/basicauthrealm.json b/examples/basic-auth/basicauthrealm.json
new file mode 100644
index 0000000..d738fd2
--- /dev/null
+++ b/examples/basic-auth/basicauthrealm.json
@@ -0,0 +1,56 @@
+{
+ "realm": "basic-auth",
+ "enabled": true,
+ "accessTokenLifespan": 60,
+ "accessCodeLifespan": 60,
+ "accessCodeLifespanUserAction": 300,
+ "ssoSessionIdleTimeout": 600,
+ "ssoSessionMaxLifespan": 36000,
+ "passwordCredentialGrantAllowed": true,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "social": false,
+ "updateProfileOnInitialSocialLogin": false,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "admin",
+ "enabled": true,
+ "email" : "admin@admin.com",
+ "firstName": "Admin",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user","admin" ],
+ "applicationRoles": {
+ "realm-management": [ "realm-admin" ]
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Administrator privileges"
+ }
+ ]
+ },
+ "applications": [
+ {
+ "name": "basic-auth-service",
+ "enabled": true,
+ "adminUrl": "/basicauth",
+ "baseUrl": "/basicauth",
+ "secret": "password"
+ }
+ ]
+
+}
examples/basic-auth/pom.xml 94(+94 -0)
diff --git a/examples/basic-auth/pom.xml b/examples/basic-auth/pom.xml
new file mode 100644
index 0000000..5e7095d
--- /dev/null
+++ b/examples/basic-auth/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.1.0.Beta2-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <name>Keycloak Examples - Basic Auth</name>
+ <artifactId>examples-basicauth</artifactId>
+ <packaging>war</packaging>
+
+ <description>
+ Keycloak Basic Auth Example
+ </description>
+
+ <repositories>
+ <repository>
+ <id>jboss</id>
+ <name>jboss repo</name>
+ <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+ </repository>
+ </repositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${keycloak.apache.httpcomponents.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>basicauth</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.as.plugins</groupId>
+ <artifactId>jboss-as-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.wildfly.plugins</groupId>
+ <artifactId>wildfly-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
examples/basic-auth/README.md 29(+29 -0)
diff --git a/examples/basic-auth/README.md b/examples/basic-auth/README.md
new file mode 100644
index 0000000..c79becb
--- /dev/null
+++ b/examples/basic-auth/README.md
@@ -0,0 +1,29 @@
+Keycloak Example - Basic Authentication
+=======================================
+
+The following example was tested on Wildfly 8.1.0.Final and JBoss EAP 6.3. It should be compatible with any JBoss AS, JBoss EAP or Wildfly that supports Java EE 7.
+
+This example demonstrates basic authentication support for a Keycloak protected REST service. However, more importantly it enables a REST service to be secured using both basic and bearer token authentication, which is useful where the service needs to be accessed both as part of a single signon session, and also as a standalone REST service.
+
+
+Step 1: Setup a basic Keycloak server
+--------------------------------------------------------------
+Install Keycloak server and start it on port 8080. Check the Reference Guide if unsure on how to do it.
+
+Once the Keycloak server is up and running, import the realm basicauthrealm.json.
+
+
+Step 2: Deploy and run the example
+--------------------------------------------------------------
+
+- Build and deploy this sample's WAR file. For this example, deploy on the same server that is running the Keycloak Server, although this is not required for real world scenarios.
+
+- Open a command window and perform the following command:
+
+ curl http://admin:password@localhost:8080/basicauth/service/echo?value=hello
+
+This should result in the value 'hello' being returned as a response.
+
+Simply change the username (currently 'admin') or password (currently 'password') in the command to see an "Unauthorized" response.
+
+
diff --git a/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthService.java b/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthService.java
new file mode 100644
index 0000000..d722da9
--- /dev/null
+++ b/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthService.java
@@ -0,0 +1,24 @@
+package org.keycloak.example.basicauth;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("service")
+public class BasicAuthService {
+ @GET
+ @NoCache
+ @Path("echo")
+ public String echo(@QueryParam("value") String value) {
+ return value;
+ }
+}
diff --git a/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthServiceApplication.java b/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthServiceApplication.java
new file mode 100644
index 0000000..c0c0382
--- /dev/null
+++ b/examples/basic-auth/src/main/java/org/keycloak/example/basicauth/BasicAuthServiceApplication.java
@@ -0,0 +1,12 @@
+package org.keycloak.example.basicauth;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Basic auth app.
+ */
+@ApplicationPath("/")
+public class BasicAuthServiceApplication extends Application
+{
+}
diff --git a/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..4502199
--- /dev/null
+++ b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,11 @@
+{
+ "realm" : "basic-auth",
+ "resource" : "basic-auth-service",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "/auth",
+ "ssl-required" : "external",
+ "enable-basic-auth" : "true",
+ "credentials": {
+ "secret": "password"
+ }
+}
diff --git a/examples/basic-auth/src/main/webapp/WEB-INF/web.xml b/examples/basic-auth/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..f25eb46
--- /dev/null
+++ b/examples/basic-auth/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>basicauth</module-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+<!-- <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint> -->
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>basic-auth</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
examples/pom.xml 1(+1 -0)
diff --git a/examples/pom.xml b/examples/pom.xml
index 61c47af..97d1b9e 100755
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -31,5 +31,6 @@
<module>providers</module>
<module>js-console</module>
<module>multi-tenant</module>
+ <module>basic-auth</module>
</modules>
</project>
integration/adapter-core/pom.xml 5(+5 -0)
diff --git a/integration/adapter-core/pom.xml b/integration/adapter-core/pom.xml
index 9262be1..e6f4b0b 100755
--- a/integration/adapter-core/pom.xml
+++ b/integration/adapter-core/pom.xml
@@ -42,6 +42,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
index 6258645..d8a2141 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
@@ -238,6 +238,16 @@ public class AdapterDeploymentContext {
}
@Override
+ public boolean isEnableBasicAuth() {
+ return delegate.isEnableBasicAuth();
+ }
+
+ @Override
+ public void setEnableBasicAuth(boolean enableBasicAuth) {
+ delegate.setEnableBasicAuth(enableBasicAuth);
+ }
+
+ @Override
public boolean isPublicClient() {
return delegate.isPublicClient();
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java
new file mode 100644
index 0000000..55dc847
--- /dev/null
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java
@@ -0,0 +1,112 @@
+package org.keycloak.adapters;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.message.BasicNameValuePair;
+import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.constants.ServiceUrlConstants;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.KeycloakUriBuilder;
+
+import java.util.List;
+
+/**
+ * Basic auth request authenticator.
+ */
+public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticator {
+ protected Logger log = Logger.getLogger(BasicAuthRequestAuthenticator.class);
+
+ public BasicAuthRequestAuthenticator(KeycloakDeployment deployment) {
+ super(deployment);
+ }
+
+ public AuthOutcome authenticate(HttpFacade exchange) {
+ List<String> authHeaders = exchange.getRequest().getHeaders("Authorization");
+ if (authHeaders == null || authHeaders.size() == 0) {
+ challenge = challengeResponse(exchange, null, null);
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+
+ tokenString = null;
+ for (String authHeader : authHeaders) {
+ String[] split = authHeader.trim().split("\\s+");
+ if (split == null || split.length != 2) continue;
+ if (!split[0].equalsIgnoreCase("Basic")) continue;
+ tokenString = split[1];
+ }
+
+ if (tokenString == null) {
+ challenge = challengeResponse(exchange, null, null);
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+
+ AccessTokenResponse atr=null;
+ try {
+ String userpw=new String(net.iharder.Base64.decode(tokenString));
+ String[] parts=userpw.split(":");
+
+ atr = getToken(parts[0], parts[1]);
+ } catch (Exception e) {
+ log.debug("Failed to obtain token", e);
+ challenge = challengeResponse(exchange, "no_token", e.getMessage());
+ return AuthOutcome.FAILED;
+ }
+
+ return authenticateToken(exchange, atr.getToken());
+ }
+
+ private AccessTokenResponse getToken(String username, String password) throws Exception {
+ AccessTokenResponse tokenResponse=null;
+ HttpClient client = new HttpClientBuilder().disableTrustManager().build();
+
+ try {
+ HttpPost post = new HttpPost(
+ KeycloakUriBuilder.fromUri(deployment.getAuthServerBaseUrl())
+ .path(ServiceUrlConstants.TOKEN_SERVICE_DIRECT_GRANT_PATH).build(deployment.getRealm()));
+ java.util.List <NameValuePair> formparams = new java.util.ArrayList <NameValuePair>();
+ formparams.add(new BasicNameValuePair("username", username));
+ formparams.add(new BasicNameValuePair("password", password));
+
+ if (deployment.isPublicClient()) {
+ formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, deployment.getResourceName()));
+ } else {
+ String authorization = BasicAuthHelper.createHeader(deployment.getResourceName(),
+ deployment.getResourceCredentials().get("secret"));
+ post.setHeader("Authorization", authorization);
+ }
+
+ UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+ post.setEntity(form);
+
+ HttpResponse response = client.execute(post);
+ int status = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ if (status != 200) {
+ throw new java.io.IOException("Bad status: " + status);
+ }
+ if (entity == null) {
+ throw new java.io.IOException("No Entity");
+ }
+ java.io.InputStream is = entity.getContent();
+ try {
+ tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
+ } finally {
+ try {
+ is.close();
+ } catch (java.io.IOException ignored) { }
+ }
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+
+ return (tokenResponse);
+ }
+
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
index 0aafa00..ccbe596 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
@@ -59,6 +59,10 @@ public class BearerTokenRequestAuthenticator {
return AuthOutcome.NOT_ATTEMPTED;
}
+ return (authenticateToken(exchange, tokenString));
+ }
+
+ protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
} catch (VerificationException e) {
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index f4b9c90..e9dfaf6 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -38,6 +38,7 @@ public class KeycloakDeployment {
protected String resourceName;
protected boolean bearerOnly;
+ protected boolean enableBasicAuth;
protected boolean publicClient;
protected Map<String, String> resourceCredentials = new HashMap<String, String>();
protected HttpClient client;
@@ -199,6 +200,14 @@ public class KeycloakDeployment {
this.bearerOnly = bearerOnly;
}
+ public boolean isEnableBasicAuth() {
+ return enableBasicAuth;
+ }
+
+ public void setEnableBasicAuth(boolean enableBasicAuth) {
+ this.enableBasicAuth = enableBasicAuth;
+ }
+
public boolean isPublicClient() {
return publicClient;
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index e03eb51..1f00b91 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -67,6 +67,7 @@ public class KeycloakDeploymentBuilder {
}
deployment.setBearerOnly(adapterConfig.isBearerOnly());
+ deployment.setEnableBasicAuth(adapterConfig.isEnableBasicAuth());
deployment.setAlwaysRefreshToken(adapterConfig.isAlwaysRefreshToken());
deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index d5c7119..bd853da 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -36,10 +36,12 @@ public abstract class RequestAuthenticator {
if (log.isTraceEnabled()) {
log.trace("--> authenticate()");
}
+
BearerTokenRequestAuthenticator bearer = createBearerTokenAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try bearer");
}
+
AuthOutcome outcome = bearer.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = bearer.getChallenge();
@@ -47,7 +49,7 @@ public abstract class RequestAuthenticator {
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
- completeAuthentication(bearer);
+ completeAuthentication(bearer, "KEYCLOAK");
log.debug("Bearer AUTHENTICATED");
return AuthOutcome.AUTHENTICATED;
} else if (deployment.isBearerOnly()) {
@@ -56,6 +58,24 @@ public abstract class RequestAuthenticator {
return AuthOutcome.NOT_ATTEMPTED;
}
+ if (deployment.isEnableBasicAuth()) {
+ BasicAuthRequestAuthenticator basicAuth = createBasicAuthAuthenticator();
+ if (log.isTraceEnabled()) {
+ log.trace("try basic auth");
+ }
+
+ outcome = basicAuth.authenticate(facade);
+ if (outcome == AuthOutcome.FAILED) {
+ challenge = basicAuth.getChallenge();
+ log.debug("BasicAuth FAILED");
+ return AuthOutcome.FAILED;
+ } else if (outcome == AuthOutcome.AUTHENTICATED) {
+ log.debug("BasicAuth AUTHENTICATED");
+ completeAuthentication(basicAuth, "BASIC");
+ return AuthOutcome.AUTHENTICATED;
+ }
+ }
+
if (log.isTraceEnabled()) {
log.trace("try oauth");
}
@@ -104,6 +124,10 @@ public abstract class RequestAuthenticator {
return new BearerTokenRequestAuthenticator(deployment);
}
+ protected BasicAuthRequestAuthenticator createBasicAuthAuthenticator() {
+ return new BasicAuthRequestAuthenticator(deployment);
+ }
+
protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
@@ -111,13 +135,13 @@ public abstract class RequestAuthenticator {
}
protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
- protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
+ protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method);
protected abstract String getHttpSessionId(boolean create);
- protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
+ protected void completeAuthentication(BearerTokenRequestAuthenticator bearer, String method) {
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null);
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, bearer.getToken()), session);
- completeBearerAuthentication(principal);
+ completeBearerAuthentication(principal, method);
}
}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
index 74e0e63..7544e66 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaRequestAuthenticator.java
@@ -84,7 +84,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
}
@Override
- protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
if (log.isDebugEnabled()) {
@@ -92,7 +92,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
}
Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles, securityContext);
request.setUserPrincipal(generalPrincipal);
- request.setAuthType("KEYCLOAK");
+ request.setAuthType(method);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
}
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
index 33795c2..1d070cf 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
@@ -6,6 +6,7 @@ import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.AuthOutcome;
import org.keycloak.adapters.AuthenticatedActionsHandler;
+import org.keycloak.adapters.BasicAuthRequestAuthenticator;
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
@@ -188,10 +189,16 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
}
protected void bearerAuthentication(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment) {
- BearerTokenRequestAuthenticator bearer = new BearerTokenRequestAuthenticator(resolvedDeployment);
- AuthOutcome outcome = bearer.authenticate(facade);
+ BearerTokenRequestAuthenticator authenticator = new BearerTokenRequestAuthenticator(resolvedDeployment);
+ AuthOutcome outcome = authenticator.authenticate(facade);
+
+ if (outcome == AuthOutcome.NOT_ATTEMPTED && resolvedDeployment.isEnableBasicAuth()) {
+ authenticator = new BasicAuthRequestAuthenticator(resolvedDeployment);
+ outcome = authenticator.authenticate(facade);
+ }
+
if (outcome == AuthOutcome.FAILED || outcome == AuthOutcome.NOT_ATTEMPTED) {
- AuthChallenge challenge = bearer.getChallenge();
+ AuthChallenge challenge = authenticator.getChallenge();
log.fine("Authentication outcome: " + outcome);
boolean challengeSent = challenge.challenge(facade);
if (!challengeSent) {
@@ -210,7 +217,7 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
}
}
- propagateSecurityContext(facade, request, resolvedDeployment, bearer);
+ propagateSecurityContext(facade, request, resolvedDeployment, authenticator);
handleAuthActions(facade, resolvedDeployment);
}
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java
index ee6ffae..c78169e 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/AbstractJettyRequestAuthenticator.java
@@ -79,7 +79,7 @@ public abstract class AbstractJettyRequestAuthenticator extends RequestAuthentic
}
@Override
- protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
this.principal = principal;
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
diff --git a/integration/keycloak-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java b/integration/keycloak-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java
index 2254dd6..9cd606c 100755
--- a/integration/keycloak-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java
+++ b/integration/keycloak-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java
@@ -65,6 +65,12 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
+ protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
+ new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
+ .setXmlName("enable-basic-auth")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
.setXmlName("public-client")
@@ -78,6 +84,7 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
+ DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
}
diff --git a/integration/keycloak-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties b/integration/keycloak-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
index 1756381..46d254d 100755
--- a/integration/keycloak-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
+++ b/integration/keycloak-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
@@ -68,6 +68,7 @@ keycloak.secure-deployment.resource=Application name
keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
keycloak.secure-deployment.credentials=Adapter credentials
keycloak.secure-deployment.bearer-only=Bearer Token Auth only
+keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
keycloak.secure-deployment.public-client=Public client
keycloak.secure-deployment.enable-cors=Enable Keycloak CORS support
keycloak.secure-deployment.client-keystore=n/a
diff --git a/integration/keycloak-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd b/integration/keycloak-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd
index ff7c16e..17d6aa6 100755
--- a/integration/keycloak-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd
+++ b/integration/keycloak-subsystem/src/main/resources/schema/wildfly-keycloak_1_0.xsd
@@ -86,6 +86,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="enable-basic-auth" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
index fdfe16f..e7884b8 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaRequestAuthenticator.java
@@ -86,7 +86,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
}
@Override
- protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
if (log.isLoggable(Level.FINE)) {
@@ -94,7 +94,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
}
Principal generalPrincipal = principalFactory.createPrincipal(request.getContext().getRealm(), principal, roles, securityContext);
request.setUserPrincipal(generalPrincipal);
- request.setAuthType("KEYCLOAK");
+ request.setAuthType(method);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
}
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
index b9761e1..5ef0734 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowRequestAuthenticator.java
@@ -20,6 +20,7 @@ import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.util.Sessions;
+
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.HttpFacade;
@@ -69,9 +70,9 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
}
@Override
- protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
KeycloakUndertowAccount account = createAccount(principal);
- securityContext.authenticationComplete(account, "KEYCLOAK", false);
+ securityContext.authenticationComplete(account, method, false);
propagateKeycloakContext(account);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java
new file mode 100644
index 0000000..252a253
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java
@@ -0,0 +1,144 @@
+package org.keycloak.testsuite.jaxrs;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.Constants;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * Test for basic authentication.
+ */
+public class JaxrsBasicAuthTest {
+
+ private static final String JAXRS_APP_URL = Constants.SERVER_ROOT + "/jaxrs-simple/res";
+
+ public static final String CONFIG_FILE_INIT_PARAM = "config-file";
+
+ @ClassRule
+ public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ ApplicationModel app = appRealm.addApplication("jaxrs-app");
+ app.setEnabled(true);
+ app.setSecret("password");
+ app.setFullScopeAllowed(true);
+
+ JaxrsBasicAuthTest.appRealm = appRealm;
+ }
+ });
+
+ @ClassRule
+ public static ExternalResource clientRule = new ExternalResource() {
+
+ @Override
+ protected void before() throws Throwable {
+ DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
+ ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
+ client = new ResteasyClientBuilder().httpEngine(engine).build();
+ }
+
+ @Override
+ protected void after() {
+ client.close();
+ }
+ };
+
+ private static ResteasyClient client;
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected WebDriver driver;
+
+ // Used for signing admin action
+ protected static RealmModel appRealm;
+
+
+ @Test
+ public void testBasic() {
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ Map<String,String> initParams = new TreeMap<String,String>();
+ initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-basicauth.json");
+ keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
+ }
+
+ });
+
+ // Send GET request without credentials, it should fail
+ Response getResp = client.target(JAXRS_APP_URL).request().get();
+ Assert.assertEquals(getResp.getStatus(), 401);
+ getResp.close();
+
+ // Send POST request without credentials, it should fail
+ Response postResp = client.target(JAXRS_APP_URL).request().post(Entity.form(new Form()));
+ Assert.assertEquals(postResp.getStatus(), 401);
+ postResp.close();
+
+ // Retrieve token
+ String incorrectAuthHeader = "Basic "+encodeCredentials("invalid-user", "password");
+
+ // Send GET request with incorrect credentials, it shojuld fail
+ getResp = client.target(JAXRS_APP_URL).request()
+ .header(HttpHeaders.AUTHORIZATION, incorrectAuthHeader)
+ .get();
+ Assert.assertEquals(getResp.getStatus(), 401);
+ getResp.close();
+
+ // Retrieve token
+ String authHeader = "Basic "+encodeCredentials("test-user@localhost", "password");
+
+ // Send GET request with token and assert it's passing
+ JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
+ .header(HttpHeaders.AUTHORIZATION, authHeader)
+ .get(JaxrsTestResource.SimpleRepresentation.class);
+ Assert.assertEquals("get", getRep.getMethod());
+
+ Assert.assertTrue(getRep.getHasUserRole());
+ Assert.assertFalse(getRep.getHasAdminRole());
+ Assert.assertFalse(getRep.getHasJaxrsAppRole());
+ // Assert that principal is ID of user (should be in UUID format)
+ UUID.fromString(getRep.getPrincipal());
+
+ // Send POST request with token and assert it's passing
+ JaxrsTestResource.SimpleRepresentation postRep = client.target(JAXRS_APP_URL).request()
+ .header(HttpHeaders.AUTHORIZATION, authHeader)
+ .post(Entity.form(new Form()), JaxrsTestResource.SimpleRepresentation.class);
+ Assert.assertEquals("post", postRep.getMethod());
+ Assert.assertEquals(getRep.getPrincipal(), postRep.getPrincipal());
+ }
+
+ private String encodeCredentials(String username, String password) {
+ String text=username+":"+password;
+ return (net.iharder.Base64.encodeBytes(text.getBytes()));
+ }
+}
diff --git a/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak-basicauth.json b/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak-basicauth.json
new file mode 100644
index 0000000..949b720
--- /dev/null
+++ b/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak-basicauth.json
@@ -0,0 +1,11 @@
+{
+ "realm": "test",
+ "resource": "jaxrs-app",
+ "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8081/auth",
+ "ssl-required" : "external",
+ "enable-basic-auth": true,
+ "credentials": {
+ "secret": "password"
+ }
+}
\ No newline at end of file