Skip to content

Commit

Permalink
Initial work
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpetisme committed Aug 22, 2019
1 parent 4b2a4b4 commit 0ec6217
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 8 deletions.
9 changes: 9 additions & 0 deletions extensions/elytron-security/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal-deployment</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
Expand All @@ -42,6 +46,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.substrate.SubstrateResourceBuildItem;
import io.quarkus.elytron.security.runtime.AuthConfig;
import io.quarkus.elytron.security.runtime.DefaultRoleDecoder;
import io.quarkus.elytron.security.runtime.MPRealmConfig;
import io.quarkus.elytron.security.runtime.PropertiesRealmConfig;
import io.quarkus.elytron.security.runtime.SecurityConfig;
import io.quarkus.elytron.security.runtime.SecurityContextPrincipal;
import io.quarkus.elytron.security.runtime.SecurityRecorder;
import io.quarkus.elytron.security.runtime.*;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.undertow.deployment.ServletExtensionBuildItem;
import io.undertow.security.idm.IdentityManager;
Expand Down Expand Up @@ -176,11 +170,37 @@ AuthConfigBuildItem configureMPRealmConfig(SecurityRecorder recorder,
return null;
}

/**
* Check to see if a JdbcRealmConfig was specified and enabled and create a
* {@linkplain org.wildfly.security.auth.realm.LegacyPropertiesSecurityRealm}
* runtime value to process the user/roles properties files. This also registers the names of the user/roles properties
* files
* to include the build artifact.
*
* @param recorder - runtime security recorder
* @param securityRealm - the producer factory for the SecurityRealmBuildItem
* @return the AuthConfigBuildItem for the realm authentication mechanism if there was an enabled JdbcRealmConfig,
* null otherwise
* @throws Exception - on any failure
*/
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
AuthConfigBuildItem configureJdbcRealmAuthConfig(SecurityRecorder recorder,
BuildProducer<SecurityRealmBuildItem> securityRealm,
BuildProducer<PasswordRealmBuildItem> passwordRealm) throws Exception {
if (security.jdbc.enabled) {
System.out.println(">>>>>>> " + security.jdbc);
JdbcRealmConfig realmConfig = security.jdbc;
// log.debugf("Configuring from JdbcRealmConfig, principal-query=%s", realmConfig.principalQuery);
return new AuthConfigBuildItem(realmConfig.getAuthConfig());
}
return null;
}

/**
* Create the deployment SecurityDomain using the SecurityRealm and AuthConfig build items that have been created.
*
* @param recorder - the runtime recorder class used to access runtime behaviors
* @param extension - the ServletExtensionBuildItem producer used to add the Undertow identity manager and auth config
* @param realms - the previously created SecurityRealm runtime values
* @return the SecurityDomain runtime value build item
* @throws Exception
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package io.quarkus.security.test;

import static org.hamcrest.Matchers.equalTo;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

/**
* Tests of BASIC authentication mechanism with an Database Identity store
*/
public class BasicAuthJdbcRealmTestCase {
static Class[] testClasses = {
TestSecureServlet.class, TestApplication.class, RolesEndpointClassLevel.class,
ParametrizedPathsResource.class, SubjectExposingResource.class
};
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(testClasses)
.addAsResource("application-jdbc-single-query-no-attribues.properties"));

// Basic @ServletSecurity tests
@Test()
public void testSecureAccessFailure() {
RestAssured.when().get("/secure-test").then()
.statusCode(401);
}

@Test()
public void testSecureRoleFailure() {
RestAssured.given().auth().preemptive().basic("jdoe", "p4ssw0rd")
.when().get("/secure-test").then()
.statusCode(403);
}

@Test()
public void testSecureAccessSuccess() {
RestAssured.given().auth().preemptive().basic("stuart", "test")
.when().get("/secure-test").then()
.statusCode(200);
}

/**
* Test access a secured jaxrs resource without any authentication. should see 401 error code.
*/
@Test
public void testJaxrsGetFailure() {
RestAssured.when().get("/jaxrs-secured/rolesClass").then()
.statusCode(401);
}

/**
* Test access a secured jaxrs resource with authentication, but no authorization. should see 403 error code.
*/
@Test
public void testJaxrsGetRoleFailure() {
RestAssured.given().auth().preemptive().basic("jdoe", "p4ssw0rd")
.when().get("/jaxrs-secured/rolesClass").then()
.statusCode(403);
}

/**
* Test access a secured jaxrs resource with authentication, and authorization. should see 200 success code.
*/
@Test
public void testJaxrsGetRoleSuccess() {
RestAssured.given().auth().preemptive().basic("scott", "jb0ss")
.when().get("/jaxrs-secured/rolesClass").then()
.statusCode(200);
}

/**
* Test access a secured jaxrs resource with authentication, and authorization. should see 200 success code.
*/
@Test
public void testJaxrsPathAdminRoleSuccess() {
RestAssured.given().auth().preemptive().basic("scott", "jb0ss")
.when().get("/jaxrs-secured/parameterized-paths/my/banking/admin").then()
.statusCode(200);
}

@Test
public void testJaxrsPathAdminRoleFailure() {
RestAssured.given().auth().preemptive().basic("noadmin", "n0Adm1n")
.when().get("/jaxrs-secured/parameterized-paths/my/banking/admin").then()
.statusCode(403);
}

/**
* Test access a secured jaxrs resource with authentication, and authorization. should see 200 success code.
*/
@Test
public void testJaxrsPathUserRoleSuccess() {
RestAssured.given().auth().preemptive().basic("stuart", "test")
.when().get("/jaxrs-secured/parameterized-paths/my/banking/view").then()
.statusCode(200);
}

/**
* Test access a secured jaxrs resource with authentication, and authorization. should see 200 success code.
*/
@Test
public void testJaxrsUserRoleSuccess() {
RestAssured.given().auth().preemptive().basic("scott", "jb0ss")
.when().get("/jaxrs-secured/subject/secured").then()
.statusCode(200)
.body(equalTo("scott"));
}

@Test
public void testJaxrsInjectedPrincipalSuccess() {
RestAssured.given().auth().preemptive().basic("scott", "jb0ss")
.when().get("/jaxrs-secured/subject/principalSecured").then()
.statusCode(200)
.body(equalTo("scott"));
}

/**
* Test access a @PermitAll secured jaxrs resource without any authentication. should see a 200 success code.
*/
@Test
public void testJaxrsGetPermitAll() {
RestAssured.when().get("/jaxrs-secured/subject/unsecured").then()
.statusCode(200)
.body(equalTo("anonymous"));
}

/**
* Test access a @DenyAll secured jaxrs resource without authentication. should see a 401 success code.
*/
@Test
public void testJaxrsGetDenyAllWithoutAuth() {
RestAssured.when().get("/jaxrs-secured/subject/denied").then()
.statusCode(401);
}

/**
* Test access a @DenyAll secured jaxrs resource with authentication. should see a 403 success code.
*/
@Test
public void testJaxrsGetDenyAllWithAuth() {
RestAssured.given().auth().preemptive().basic("scott", "jb0ss")
.when().get("/jaxrs-secured/subject/denied").then()
.statusCode(403);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
quarkus.datasource.url=jdbc:h2:tcp://localhost/mem:default
quarkus.datasource.driver=org.h2.Driver
quarkus.datasource.username=username-default

quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=
quarkus.security.jdbc.principal-query.datasource=default
4 changes: 4 additions & 0 deletions extensions/elytron-security/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.substratevm</groupId>
<artifactId>svm</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.quarkus.elytron.security.runtime;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

/**
* A configuration object for a jdbc based realm configuration,
* {@linkplain org.wildfly.security.auth.realm.LegacyPropertiesSecurityRealm}
*/
@ConfigGroup
public class JdbcRealmConfig {

/**
* The authentication mechanism
*/
@ConfigItem(defaultValue = "BASIC")
public String authMechanism;

/**
* The authentication mechanism
*/
@ConfigItem(defaultValue = "Quarkus")
public String realmName;

/**
* If the properties store is enabled.
*/
@ConfigItem
public boolean enabled;

/**
* The principal-query config
*/
@ConfigItem
public PrincipalQueryConfig principalQuery;
// https://github.com/wildfly/wildfly-core/blob/master/elytron/src/test/resources/org/wildfly/extension/elytron/security-realms.xml#L18

public String getAuthMechanism() {
return authMechanism;
}

public void setAuthMechanism(String authMechanism) {
this.authMechanism = authMechanism;
}

public String getRealmName() {
return realmName;
}

public void setRealmName(String realmName) {
this.realmName = realmName;
}

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public PrincipalQueryConfig getPrincipalQuery() {
return principalQuery;
}

public void setPrincipalQuery(PrincipalQueryConfig principalQuery) {
this.principalQuery = principalQuery;
}

/**
* Used to access what should be a parent class, but parsing of the MP config properties is not working
* from parent to child
*
* @return AuthConfig information
*/
public AuthConfig getAuthConfig() {
return new AuthConfig(authMechanism, realmName, getClass());
}

@Override
public String toString() {
return "JdbcRealmConfig{" +
"authMechanism='" + authMechanism + '\'' +
", realmName='" + realmName + '\'' +
", enabled=" + enabled +
", principalQuery=" + principalQuery +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.elytron.security.runtime;

import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;

@ConfigGroup
public class PrincipalQueryConfig {

/**
* The sql query to find the password
*/
@ConfigItem(defaultValue = "SELECT password FROM users WHERE username=?")
public String sql;

/**
* The data source to use
*/
@ConfigItem(defaultValue = "default")
public String datasource;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public final class SecurityConfig {
*/
@ConfigItem
public MPRealmConfig embedded;
/**
* The configuration for the {@linkplain org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm}
*/
@ConfigItem
public JdbcRealmConfig jdbc;

/**
* List of security providers to enable for reflection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,26 @@ public Provider[] get() {
return new RuntimeValue<>(realm);
}

/**
* Create a runtime value for a {@linkplain SimpleMapBackedSecurityRealm}
*
* @param config - the realm config
* @return - runtime value wrapper for the SecurityRealm
* @throws Exception
*/
public RuntimeValue<SecurityRealm> createRealm(JdbcRealmConfig config) {
log.debugf("createRealm, config=%s", config);

Supplier<Provider[]> providers = new Supplier<Provider[]>() {
@Override
public Provider[] get() {
return new Provider[] { new WildFlyElytronProvider() };
}
};
SecurityRealm realm = new SimpleMapBackedSecurityRealm(NameRewriter.IDENTITY_REWRITER, providers);
return new RuntimeValue<>(realm);
}

/**
* Create a {@linkplain SecurityDomain.Builder} for the given default {@linkplain SecurityRealm}.
*
Expand Down

0 comments on commit 0ec6217

Please sign in to comment.