Skip to content

Commit

Permalink
Merge pull request wildfly#18130 from rsearls/WFLY-19327-FORM-and-OID…
Browse files Browse the repository at this point in the history
…C-auth

[WFLY-19327] Validate it's possible to secure two apps within the same EAR with FORM and OIDC credential support
  • Loading branch information
bstansberry authored Sep 25, 2024
2 parents 95958a5 + e0a255b commit 34c0a52
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
class OidcActivationProcessor implements DeploymentUnitProcessor {

private static final String OIDC_AUTH_METHOD = "OIDC";
public static final String OIDC_AUTH_METHOD = "OIDC";

@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.jboss.as.server.security.AdvancedSecurityMetaData;
import org.jboss.as.server.security.SecurityMetaData;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.metadata.web.spec.LoginConfigMetaData;
import org.jboss.msc.service.ServiceName;

/**
Expand All @@ -35,8 +36,12 @@ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitPro
if (warMetaData == null) {
return;
}

LoginConfigMetaData loginConfig = warMetaData.getMergedJBossWebMetaData().getLoginConfig();
SecurityMetaData securityMetaData = deploymentUnit.getAttachment(ATTACHMENT_KEY);
if (securityMetaData != null && isVirtualMechanismFactoryRequired(deploymentUnit)) {
if (securityMetaData != null
&& (loginConfig != null && OidcActivationProcessor.OIDC_AUTH_METHOD.equals(loginConfig.getAuthMethod()))
&& isVirtualMechanismFactoryRequired(deploymentUnit)) {
AdvancedSecurityMetaData advancedSecurityMetaData = new AdvancedSecurityMetaData();
advancedSecurityMetaData.setHttpServerAuthenticationMechanismFactory(virtualMechanismFactoryName(deploymentUnit));
ServiceName virtualDomainName = virtualDomainName(deploymentUnit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@
import static org.wildfly.security.http.oidc.Oidc.AuthenticationRequestFormat.OAUTH2;
import static org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALLOWED_ORIGIN;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import static org.apache.http.HttpStatus.SC_OK;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
Expand All @@ -43,6 +46,7 @@
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
Expand All @@ -52,6 +56,7 @@
import org.jboss.as.test.integration.management.ManagementOperations;
import org.jboss.as.test.integration.security.common.servlets.SimpleSecuredServlet;
import org.jboss.as.test.integration.security.common.servlets.SimpleServlet;
import org.jboss.as.test.shared.ManagementServerSetupTask;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.as.test.shared.util.AssumeTestGroupUtil;
import org.jboss.as.version.Stability;
Expand All @@ -64,6 +69,7 @@
import org.keycloak.representations.idm.RealmRepresentation;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.jose.util.JsonSerialization;
import org.wildfly.test.integration.elytron.oidc.client.deployment.OidcWithDeploymentConfigTest;
import org.wildfly.test.integration.elytron.oidc.client.subsystem.SimpleServletWithScope;

import io.restassured.RestAssured;
Expand All @@ -87,6 +93,8 @@ public abstract class OidcBaseTest {
public static final String AUTH_SERVER_URL_APP = "AuthServerUrlOidcApp";
public static final String WRONG_PROVIDER_URL_APP = "WrongProviderUrlOidcApp";
public static final String WRONG_SECRET_APP = "WrongSecretOidcApp";
public static final String FORM_WITH_OIDC_EAR_APP = "FormWithOidcApp";
public static final String FORM_WITH_OIDC_OIDC_APP = "oidc";
public static final String DIRECT_ACCCESS_GRANT_ENABLED_CLIENT = "DirectAccessGrantEnabledClient";
public static final String BEARER_ONLY_AUTH_SERVER_URL_APP = "AuthServerUrlBearerOnlyApp";
public static final String BEARER_ONLY_PROVIDER_URL_APP = "ProviderUrlBearerOnlyApp";
Expand Down Expand Up @@ -117,6 +125,20 @@ public abstract class OidcBaseTest {
public static final String INVALID_SIGNATURE_ALGORITHM_APP = "InvalidSignatureAlgorithmApp";
public static final String PS_SIGNED_REQUEST_URI_APP = "PsSignedRequestUriApp";
public static final String MISSING_SECRET_APP = "MissingSecretApp";
public static final String FORM_USER="user1";
public static final String FORM_PASSWORD="password1";
protected static final String ERROR_PAGE_CONTENT = "Error!";

// Avoid problem on windows with path
public static final String USERS_PATH = new File(
OidcWithDeploymentConfigTest.class.getResource("users.properties").getFile()).getAbsolutePath()
.replace("\\", "/");
public static final String ROLES_PATH = new File(
OidcWithDeploymentConfigTest.class.getResource("roles.properties").getFile()).getAbsolutePath()
.replace("\\", "/");
public static final String ORIGINAL_USERS_PATH = "application-users.properties";
public static final String ORIGINAL_ROLES_PATH = "application-roles.properties";
public static final String RELATIVE_TO = "jboss.server.config.dir";

private final Stability desiredStability;

Expand Down Expand Up @@ -493,6 +515,71 @@ public void testOpenIDWithMissingSecretHmacSigningAlgorithm() throws Exception {
"/" + MISSING_SECRET_APP + SimpleSecuredServlet.SERVLET_PATH).toURI(), true);
}

@Test
@OperateOnDeployment(FORM_WITH_OIDC_EAR_APP)
public void testFormWithOidc() throws Exception {
// oidc login
// EAR declares context-root to be oidc
loginToApp(FORM_WITH_OIDC_OIDC_APP,
org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALICE,
org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALICE_PASSWORD,
HttpURLConnection.HTTP_OK, SimpleServlet.RESPONSE_BODY);

// login with Form wfly user acct
testFormCredentials();
}
private void testFormCredentials() throws Exception {
URI requestUri = new URI("http://"+CLIENT_HOST_NAME+":"+CLIENT_PORT
+"/form"+"/"+SimpleSecuredServlet.class.getSimpleName()
+"/j_security_check");
HttpClient httpClient = HttpClients.createDefault();
HttpPost getMethod = new HttpPost(requestUri);

List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("j_username", FORM_USER));
nvps.add(new BasicNameValuePair("j_password", FORM_PASSWORD));

getMethod.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));

HttpResponse response = httpClient.execute(getMethod);
int statusCode = response.getStatusLine().getStatusCode();
assertTrue("Expected code == OK but got " + statusCode +
" for request=" + requestUri, statusCode == HttpURLConnection.HTTP_MOVED_TEMP);
}

@Test
@OperateOnDeployment(FORM_WITH_OIDC_EAR_APP)
public void testInvalidFormWithOidcCredentials() throws Exception {
// login with Form wfly user acct
testInvalidFormCredentials();
// oidc login
// EAR declares context-root to be oidc
loginToApp(FORM_WITH_OIDC_OIDC_APP,
org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration.ALICE,
"WRONG_PASSWORD", HttpURLConnection.HTTP_OK, "Invalid username or password");
}
public void testInvalidFormCredentials() throws Exception {
URI requestUri = new URI("http://"+CLIENT_HOST_NAME+":"+CLIENT_PORT
+"/form"+"/"+SimpleSecuredServlet.class.getSimpleName()
+"/j_security_check");
HttpClient httpClient = HttpClients.createDefault();
HttpPost getMethod = new HttpPost(requestUri);

List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("j_username", "Not"+FORM_USER));
nvps.add(new BasicNameValuePair("j_password", "Not"+FORM_PASSWORD));

getMethod.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));

HttpResponse response = httpClient.execute(getMethod);
int statusCode = response.getStatusLine().getStatusCode();
assertEquals("For request=" + requestUri +" Unexpected status code in HTTP response.",
SC_OK, statusCode );
String errorMsg = EntityUtils.toString(response.getEntity());
assertTrue("Expected HTTP response to contain " + ERROR_PAGE_CONTENT
+ " response msg is: " + errorMsg, errorMsg.contains(ERROR_PAGE_CONTENT));
}

public static void loginToApp(String appName, String username, String password, int expectedStatusCode, String expectedText) throws Exception {
loginToApp(username, password, expectedStatusCode, expectedText, true,
new URL("http", TestSuiteEnvironment.getHttpAddress(), TestSuiteEnvironment.getHttpPort(),
Expand Down Expand Up @@ -886,4 +973,35 @@ protected static <T extends OidcBaseTest> void addSystemProperty(ManagementClien
add.get(VALUE).set(clazz.getName());
ManagementOperations.executeOperation(client.getControllerClient(), add);
}

public static class WildFlyServerSetupTask extends ManagementServerSetupTask {
public WildFlyServerSetupTask() {
super(createContainerConfigurationBuilder()
.setupScript(createScriptBuilder()
.startBatch()
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=users-properties.path,value=\"%s\")",
USERS_PATH))
.add("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=users-properties.plain-text,value=true)")
.add("/subsystem=elytron/properties-realm=ApplicationRealm:undefine-attribute(name=users-properties.relative-to)")
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=groups-properties.path,value=\"%s\")",
ROLES_PATH))
.add("/subsystem=elytron/properties-realm=ApplicationRealm:undefine-attribute(name=groups-properties.relative-to)")
.endBatch()
.build())
.tearDownScript(createScriptBuilder()
.startBatch()
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=users-properties.path,value=\"%s\")",
ORIGINAL_USERS_PATH))
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=users-properties.relative-to,value=\"%s\")",
RELATIVE_TO))
.add("/subsystem=elytron/properties-realm=ApplicationRealm:undefine-attribute(name=users-properties.plain-text)")
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=groups-properties.path,value=\"%s\")",
ORIGINAL_ROLES_PATH))
.add(String.format("/subsystem=elytron/properties-realm=ApplicationRealm:write-attribute(name=groups-properties.relative-to,value=\"%s\")",
RELATIVE_TO))
.endBatch()
.build())
.build());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.jboss.dmr.ModelNode;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
import org.jboss.shrinkwrap.api.Archive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.test.integration.elytron.oidc.client.KeycloakConfiguration;
Expand All @@ -45,7 +47,9 @@
*/
@RunWith(Arquillian.class)
@RunAsClient
@ServerSetup({ OidcWithDeploymentConfigTest.PreviewStabilitySetupTask.class, OidcWithDeploymentConfigTest.KeycloakAndSystemPropertySetup.class })
@ServerSetup({ OidcWithDeploymentConfigTest.PreviewStabilitySetupTask.class,
OidcWithDeploymentConfigTest.KeycloakAndSystemPropertySetup.class,
OidcBaseTest.WildFlyServerSetupTask.class})
public class OidcWithDeploymentConfigTest extends OidcBaseTest {

private static final String OIDC_PROVIDER_URL = "oidc.provider.url";
Expand Down Expand Up @@ -115,6 +119,7 @@ public class OidcWithDeploymentConfigTest extends OidcBaseTest {
APP_NAMES.put(PS_SIGNED_REQUEST_URI_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT);
APP_NAMES.put(INVALID_SIGNATURE_ALGORITHM_FILE, KeycloakConfiguration.ClientAppType.OIDC_CLIENT);
APP_NAMES.put(MISSING_SECRET_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT);
APP_NAMES.put(FORM_WITH_OIDC_OIDC_APP, KeycloakConfiguration.ClientAppType.OIDC_CLIENT);
}

public OidcWithDeploymentConfigTest() {
Expand Down Expand Up @@ -350,6 +355,39 @@ public static WebArchive createOpenIDWithMissingSecretHmacSigningAlgorithm() {
.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(), MISSING_SECRET_WITH_HMAC_ALGORITHM_FILE, "oidc.json");
}

@Deployment(name = FORM_WITH_OIDC_EAR_APP, managed = false, testable = false)
public static Archive<?> createFormWithOidcDeployment() {
final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, FORM_WITH_OIDC_EAR_APP+".ear");
ear.addAsManifestResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP+"_application.xml", "application.xml");

final WebArchive form = ShrinkWrap.create(WebArchive.class, "form.war");
form.addClasses(SimpleServlet.class);
form.addClasses(SimpleSecuredServlet.class);
form.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP + "_form_web.xml", "web.xml");
form.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP + "_form_jboss-web.xml", "jboss-web.xml");
form.addAsWebResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP + "_login.jsp", "login.jsp");
form.addAsWebResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP + "_error.jsp", "error.jsp");

ear.addAsModule(form);

final WebArchive oidc = ShrinkWrap.create(WebArchive.class, "oidc.war");
oidc.addClasses(SimpleServlet.class);
oidc.addClasses(SimpleSecuredServlet.class);
oidc.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP+"_oidc_web.xml", "web.xml");
oidc.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP+"_oidc_jboss-web.xml", "jboss-web.xml");
oidc.addAsWebInfResource(OidcWithDeploymentConfigTest.class.getPackage(),
FORM_WITH_OIDC_EAR_APP+"_oidc_oidc.json", "oidc.json");
ear.addAsModule(oidc);
return ear;
}

@Test
@InSequence(1)
public void testWrongPasswordWithProviderUrl() throws Exception {
Expand Down Expand Up @@ -567,6 +605,28 @@ public void testCorsRequestWithEnableCorsWithInvalidOrigin() throws Exception {
}
}

@Test
@InSequence(27)
public void testFormWithOidc() throws Exception {
try {
deployer.deploy(FORM_WITH_OIDC_EAR_APP);
super.testFormWithOidc();
} finally {
deployer.undeploy(FORM_WITH_OIDC_EAR_APP);
}
}

@Test
@InSequence(28)
public void testInvalidFormWithOidcCredentials() throws Exception {
try {
deployer.deploy(FORM_WITH_OIDC_EAR_APP);
super.testInvalidFormWithOidcCredentials();
} finally {
deployer.undeploy(FORM_WITH_OIDC_EAR_APP);
}
}

@Test
public void testOpenIDScope() throws Exception {
try{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright The WildFly Authors
# SPDX-License-Identifier: Apache-2.0
#

## rls user1=role1
user1=JBossAdmin
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# Copyright The WildFly Authors
# SPDX-License-Identifier: Apache-2.0
#

user1=password1
Loading

0 comments on commit 34c0a52

Please sign in to comment.