Skip to content

Commit 065c6d0

Browse files
committed
[UNDERTOW-1881] - Add a new exchange attribute for SSL/TLS protocol version
Add and register new ExchangeAttribute implementation Add support for AJP and TLS Add system property to help identify proxied tests Add test case
1 parent 6ae61c6 commit 065c6d0

File tree

5 files changed

+184
-2
lines changed

5 files changed

+184
-2
lines changed

core/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<artifactId>junit</artifactId>
103103
<scope>test</scope>
104104
</dependency>
105-
105+
106106
<dependency>
107107
<groupId>org.apache.directory.server</groupId>
108108
<artifactId>apacheds-test-framework</artifactId>
@@ -530,6 +530,11 @@
530530
</configuration>
531531
</execution>
532532
</executions>
533+
<configuration>
534+
<systemPropertyVariables>
535+
<undertow.proxied>true</undertow.proxied>
536+
</systemPropertyVariables>
537+
</configuration>
533538
</plugin>
534539
</plugins>
535540
</build>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2024 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package io.undertow.attribute;
20+
21+
import javax.net.ssl.SSLSession;
22+
23+
import io.undertow.server.HttpServerExchange;
24+
import io.undertow.server.SSLSessionInfo;
25+
import io.undertow.util.HeaderValues;
26+
27+
public class SecureProtocolAttribute implements ExchangeAttribute {
28+
29+
public static final SecureProtocolAttribute INSTANCE = new SecureProtocolAttribute();
30+
31+
@Override
32+
public String readAttribute(HttpServerExchange exchange) {
33+
String secureProtocol = null;
34+
String transportProtocol = exchange.getConnection().getTransportProtocol();
35+
if ("ajp".equals(transportProtocol)) {
36+
// TODO: wrong
37+
HeaderValues headerValues = exchange.getRequestHeaders().get("AJP_SSL_PROTOCOL");
38+
if (headerValues != null && !headerValues.isEmpty()) {
39+
secureProtocol = headerValues.getFirst();
40+
}
41+
} else {
42+
SSLSessionInfo ssl = exchange.getConnection().getSslSessionInfo();
43+
if (ssl == null) {
44+
return null;
45+
}
46+
SSLSession session = ssl.getSSLSession();
47+
if (session != null) {
48+
secureProtocol = session.getProtocol();
49+
}
50+
}
51+
52+
return secureProtocol;
53+
}
54+
55+
@Override
56+
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
57+
throw new ReadOnlyAttributeException("Secure Protocol", newValue);
58+
}
59+
60+
@Override
61+
public String toString() {
62+
return "%{SECURE_PROTOCOL}";
63+
}
64+
65+
public static final class Builder implements ExchangeAttributeBuilder {
66+
67+
@Override
68+
public String name() {
69+
return "Secure Protocol";
70+
}
71+
72+
@Override
73+
public ExchangeAttribute build(final String token) {
74+
if (token.equals("%{SECURE_PROTOCOL}")) {
75+
return INSTANCE;
76+
}
77+
return null;
78+
}
79+
80+
@Override
81+
public int priority() {
82+
return 0;
83+
}
84+
}
85+
}

core/src/main/java/io/undertow/server/handlers/accesslog/DefaultAccessLogReceiver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public void run() {
234234
* <p>
235235
* DO NOT USE THIS OUTSIDE OF A TEST
236236
*/
237-
void awaitWrittenForTest() throws InterruptedException {
237+
protected void awaitWrittenForTest() throws InterruptedException {
238238
while (!pendingMessages.isEmpty() || forceLogRotation) {
239239
Thread.sleep(10);
240240
}

core/src/main/resources/META-INF/services/io.undertow.attribute.ExchangeAttributeBuilder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ io.undertow.attribute.PredicateContextAttribute$Builder
2323
io.undertow.attribute.QueryParameterAttribute$Builder
2424
io.undertow.attribute.SslClientCertAttribute$Builder
2525
io.undertow.attribute.SslCipherAttribute$Builder
26+
io.undertow.attribute.SecureProtocolAttribute$Builder
2627
io.undertow.attribute.SslSessionIdAttribute$Builder
2728
io.undertow.attribute.ResponseTimeAttribute$Builder
2829
io.undertow.attribute.PathParameterAttribute$Builder
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2024 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package io.undertow.server.ssl;
19+
20+
import java.io.IOException;
21+
import java.nio.file.Path;
22+
import java.nio.file.Paths;
23+
import java.util.concurrent.Executor;
24+
import javax.net.ssl.SSLContext;
25+
26+
import io.undertow.attribute.ExchangeAttribute;
27+
import io.undertow.attribute.ExchangeAttributes;
28+
import io.undertow.attribute.SubstituteEmptyWrapper;
29+
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
30+
import io.undertow.testutils.DefaultServer;
31+
import io.undertow.testutils.HttpClientUtils;
32+
import io.undertow.testutils.TestHttpClient;
33+
import io.undertow.util.CompletionLatchHandler;
34+
import io.undertow.util.StatusCodes;
35+
import org.apache.http.HttpResponse;
36+
import org.apache.http.client.methods.HttpGet;
37+
import org.junit.Assert;
38+
import org.junit.AssumptionViolatedException;
39+
import org.junit.Test;
40+
import org.junit.runner.RunWith;
41+
42+
@RunWith(DefaultServer.class)
43+
public class SecureProtocolAttributeTestCase {
44+
private static final Path logDirectory = Paths.get(System.getProperty("java.io.tmpdir"), "logs");
45+
46+
@Test
47+
public void testTlsRequestViaLogging() throws IOException {
48+
if (System.getProperty("undertow.proxied") != null) {
49+
throw new AssumptionViolatedException("This test makes no sense in a proxy environment");
50+
}
51+
LocalAccessLogReceiver logReceiver
52+
= new LocalAccessLogReceiver(DefaultServer.getWorker(), logDirectory, "server", ".log");
53+
54+
final String formatString = "Secure Protocol is %{SECURE_PROTOCOL}.";
55+
CompletionLatchHandler latchHandler = new CompletionLatchHandler(
56+
exchange -> {
57+
ExchangeAttribute tokens = ExchangeAttributes.parser(SecureProtocolAttributeTestCase.class.getClassLoader(),
58+
new SubstituteEmptyWrapper("-")).parse(formatString);
59+
exchange.getResponseSender().send(tokens.readAttribute(exchange));
60+
});
61+
62+
DefaultServer.setRootHandler(latchHandler);
63+
64+
try (TestHttpClient client = new TestHttpClient()) {
65+
DefaultServer.startSSLServer();
66+
SSLContext sslContext = DefaultServer.getClientSSLContext();
67+
client.setSSLContext(sslContext);
68+
69+
HttpResponse result = client.execute(new HttpGet(DefaultServer.getDefaultServerSSLAddress() + "/path"));
70+
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
71+
Assert.assertEquals(formatString.replaceAll("%\\{SECURE_PROTOCOL}", sslContext.getProtocol()),
72+
HttpClientUtils.readResponse(result));
73+
} finally {
74+
DefaultServer.stopSSLServer();
75+
}
76+
}
77+
78+
private static class LocalAccessLogReceiver extends DefaultAccessLogReceiver {
79+
LocalAccessLogReceiver(final Executor logWriteExecutor,
80+
final Path outputDirectory,
81+
final String logBaseName,
82+
final String logNameSuffix) {
83+
super(logWriteExecutor, outputDirectory, logBaseName, logNameSuffix, true);
84+
}
85+
86+
protected void awaitWrittenForTest() throws InterruptedException {
87+
super.awaitWrittenForTest();
88+
}
89+
}
90+
91+
}

0 commit comments

Comments
 (0)