Skip to content

Commit

Permalink
Merge pull request #3286 from jamezp/RESTEASY-3228
Browse files Browse the repository at this point in the history
[RESTEASY-3228] Enable a CI job for testing with the security manager enabled and fix issues
  • Loading branch information
jamezp committed Sep 30, 2022
2 parents 18a0512 + a607067 commit c9a75d2
Show file tree
Hide file tree
Showing 82 changed files with 1,315 additions and 133 deletions.
35 changes: 34 additions & 1 deletion .github/workflows/maven.yml
Expand Up @@ -23,7 +23,7 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest ]
java: ['11', '17']
wildfly-version: ['27.0.0.Alpha5']
wildfly-version: ['27.0.0.Beta1']

steps:
- uses: actions/checkout@v3
Expand All @@ -49,6 +49,39 @@ jobs:
name: server-logs-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.wildfly-version }}
path: '**/server.log'

test-with-security-manager:
runs-on: ubuntu-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
java: ['11']
wildfly-version: ['27.0.0.Beta1']

steps:
- uses: actions/checkout@v3
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Security Manager Tests - JDK ${{ matrix.java }} - WildFly ${{ matrix.wildfly-version }}
run: |
echo "::group::Build Logs"
mvn clean install -U -B -fae '-Dsecurity.manager' '-Dserver.version=${{ matrix.wildfly-version }}' '-Dgithub.actions'
echo "::endgroup::"
- uses: actions/upload-artifact@v3
if: failure()
with:
name: surefire-reports-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.wildfly-version }}
path: '**/surefire-reports/*.txt'
- uses: actions/upload-artifact@v3
if: failure()
with:
name: server-logs-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.wildfly-version }}
path: '**/server.log'

build-java-docs:
runs-on: ubuntu-latest
timeout-minutes: 20
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Expand Up @@ -24,7 +24,7 @@
<dep.arquillian-bom.version>1.7.0.Alpha12</dep.arquillian-bom.version>
<version.org.jboss.arquillian.container.arquillian-weld-embedded>2.0.0.Final</version.org.jboss.arquillian.container.arquillian-weld-embedded>
<version.org.jboss.resteasy.checkstyle>1.0.0.Final</version.org.jboss.resteasy.checkstyle>
<server.version>27.0.0.Alpha5</server.version>
<server.version>27.0.0.Beta1</server.version>
<version.org.wildfly>${server.version}</version.org.wildfly>
<version.org.wildfly.arquillian.wildfly-arquillian>5.0.0.Alpha5</version.org.wildfly.arquillian.wildfly-arquillian>

Expand Down Expand Up @@ -60,7 +60,7 @@
<version.surefire.plugin>3.0.0-M7</version.surefire.plugin>

<!-- Test options -->
<additionalJvmArgs/>
<additionalJvmArgs>-Djava.io.tmpdir=${project.build.directory}</additionalJvmArgs>
</properties>

<url>https://resteasy.dev</url>
Expand Down Expand Up @@ -240,7 +240,7 @@
</activation>
<properties>
<surefire.system.args>-Xms512m -Xmx512m -Djdk.io.File.enableADS=true</surefire.system.args>
<additionalJvmArgs>-Djdk.io.File.enableADS=true</additionalJvmArgs>
<additionalJvmArgs>-Djdk.io.File.enableADS=true -Djava.io.tmpdir=${project.build.directory}</additionalJvmArgs>
</properties>
</profile>
</profiles>
Expand Down
@@ -1,5 +1,7 @@
package org.jboss.resteasy.plugins.providers.jackson;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.StringTokenizer;

import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
Expand All @@ -16,20 +18,15 @@ public class WhiteListPolymorphicTypeValidatorBuilder extends BasicPolymorphicTy

public WhiteListPolymorphicTypeValidatorBuilder() {
super();
Configuration c = ConfigurationFactory.getInstance().getConfiguration();
String allowIfBaseType = c.getOptionalValue(BASE_TYPE_PROP, String.class)
.or(() -> c.getOptionalValue(BASE_TYPE_PROP + ".prefix", String.class))
.orElse(null);
String allowIfBaseType = getProperty(BASE_TYPE_PROP);
if (allowIfBaseType != null) {
StringTokenizer st = new StringTokenizer(allowIfBaseType, ",", false);
while (st.hasMoreTokens()) {
String t = st.nextToken();
allowIfBaseType("*".equals(t) ? "" : t);
}
}
String allowIfSubType = c.getOptionalValue(SUB_TYPE_PROP, String.class)
.or(() -> c.getOptionalValue(SUB_TYPE_PROP + ".prefix", String.class))
.orElse(null);
String allowIfSubType = getProperty(SUB_TYPE_PROP);
if (allowIfSubType != null) {
StringTokenizer st = new StringTokenizer(allowIfSubType, ",", false);
while (st.hasMoreTokens()) {
Expand All @@ -38,4 +35,19 @@ public WhiteListPolymorphicTypeValidatorBuilder() {
}
}
}

private static String getProperty(final String name) {
if (System.getSecurityManager() == null) {
final Configuration config = ConfigurationFactory.getInstance().getConfiguration();
return config.getOptionalValue(name, String.class)
.or(() -> config.getOptionalValue(name + ".prefix", String.class))
.orElse(null);
}
return AccessController.doPrivileged((PrivilegedAction<String>) () -> {
final Configuration config = ConfigurationFactory.getInstance().getConfiguration();
return config.getOptionalValue(name, String.class)
.or(() -> config.getOptionalValue(name + ".prefix", String.class))
.orElse(null);
});
}
}
Expand Up @@ -8,10 +8,12 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;

import org.apache.james.mime4j.MimeException;
Expand All @@ -34,7 +36,6 @@
import org.apache.james.mime4j.stream.BodyDescriptorBuilder;
import org.apache.james.mime4j.stream.MimeConfig;
import org.jboss.resteasy.plugins.providers.multipart.i18n.LogMessages;
import org.jboss.resteasy.spi.config.Configuration;
import org.jboss.resteasy.spi.config.ConfigurationFactory;

/**
Expand Down Expand Up @@ -65,7 +66,8 @@ public static Message parseMessage(InputStream is) throws IOException, MimeIOExc
BodyDescriptorBuilder bdb = new DefaultBodyDescriptorBuilder(null, strict ? DefaultFieldParser.getParser() : LenientFieldParser.getParser(), mon);

StorageProvider storageProvider;
if (ConfigurationFactory.getInstance().getConfiguration().getOptionalValue(DefaultStorageProvider.DEFAULT_STORAGE_PROVIDER_PROPERTY, String.class).orElse(null) != null) {
final Optional<String> value = getProperty(DefaultStorageProvider.DEFAULT_STORAGE_PROVIDER_PROPERTY, String.class);
if (value.isPresent()) {
storageProvider = DefaultStorageProvider.getInstance();
} else {
StorageProvider backend = new CustomTempFileStorageProvider();
Expand All @@ -91,9 +93,7 @@ static int getMemThreshold()
{
try
{
Configuration cfg = ConfigurationFactory.getInstance().getConfiguration();
int threshold = Integer.parseInt(cfg.getOptionalValue(MEM_THRESHOLD_PROPERTY, String.class).orElse(
Integer.toString(DEFAULT_MEM_THRESHOLD)));
int threshold = getProperty(MEM_THRESHOLD_PROPERTY, int.class).orElse(DEFAULT_MEM_THRESHOLD);
if (threshold > -1)
{
return threshold;
Expand All @@ -107,6 +107,19 @@ static int getMemThreshold()
return DEFAULT_MEM_THRESHOLD;
}

private static <T> Optional<T> getProperty(final String name, final Class<T> type) {
if (System.getSecurityManager() == null) {
return ConfigurationFactory.getInstance()
.getConfiguration()
.getOptionalValue(name, type);
}
return AccessController.doPrivileged((PrivilegedAction<Optional<T>>) () ->
ConfigurationFactory.getInstance()
.getConfiguration()
.getOptionalValue(name, type)
);
}

/**
* A custom TempFileStorageProvider that do no set deleteOnExit on temp files,
* to avoid memory leaks (see https://issues.apache.org/jira/browse/MIME4J-251)
Expand Down
@@ -0,0 +1,54 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2022 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.resteasy.client.exception;

import java.security.AccessController;
import java.security.PrivilegedAction;

import org.jboss.resteasy.spi.config.ConfigurationFactory;

/**
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
class SecurityActions {

/**
* Retrieves a property from the {@link org.jboss.resteasy.spi.config.Configuration}. If the security manager is
* present, a privileged action is used to get the property.
*
* @param name the name of the property
* @param type the type of the property
* @param dft the default value if not found
*
* @return the value, if found, or the default
*/
@SuppressWarnings("SameParameterValue")
static <T> T getConfigValue(final String name, final Class<T> type, final T dft) {
if (System.getSecurityManager() == null) {
return ConfigurationFactory.getInstance().getConfiguration()
.getOptionalValue(name, type)
.orElse(dft);
}
return AccessController.doPrivileged((PrivilegedAction<T>) () ->
ConfigurationFactory.getInstance().getConfiguration()
.getOptionalValue(name, type)
.orElse(dft));
}
}
Expand Up @@ -35,8 +35,6 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.spi.config.Configuration;
import org.jboss.resteasy.spi.config.ConfigurationFactory;
import org.jboss.resteasy.spi.ResteasyDeployment;

/**
Expand All @@ -58,8 +56,7 @@ public interface WebApplicationExceptionWrapper<T extends WebApplicationExceptio
* wrapping feature is turned off
*/
static WebApplicationException wrap(final WebApplicationException e) {
final Configuration config = ConfigurationFactory.getInstance().getConfiguration();
final boolean originalBehavior = config.getOptionalValue("resteasy.original.webapplicationexception.behavior", boolean.class).orElse(false);
final boolean originalBehavior = SecurityActions.getConfigValue("resteasy.original.webapplicationexception.behavior", boolean.class, false);
final boolean serverSide = ResteasyDeployment.onServer();
if (originalBehavior || !serverSide) {
return e;
Expand Down
Expand Up @@ -49,8 +49,8 @@
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
* An Apache HTTP engine for use with the new Builder Config style.
Expand Down Expand Up @@ -205,9 +205,7 @@ private ManualClosingApacheHttpClient43Engine(final HttpClient httpClient,

try
{
int threshold = Integer.parseInt(ConfigurationFactory.getInstance().getConfiguration()
.getOptionalValue(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, String.class)
.orElse("1"));
int threshold = getProperty(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, Integer.class, () -> 1);
if (threshold > -1)
{
this.fileUploadInMemoryThresholdLimit = threshold;
Expand Down Expand Up @@ -753,14 +751,15 @@ private static Cleaner.Cleanable createCleanable(final ManualClosingApacheHttpCl
}

private static Path getTempDir() {
return Path.of(getProperty("java.io.tmpdir", String.class, () -> System.getProperty("java.io.tmpdir")));
}

private static <T> T getProperty(final String name, final Class<T> type, final Supplier<T> dft) {
if (System.getSecurityManager() == null) {
final Optional<String> value = ConfigurationFactory.getInstance().getConfiguration().getOptionalValue("java.io.tmpdir", String.class);
return value.map(Path::of).orElseGet(() -> Path.of(System.getProperty("java.io.tmpdir")));
return ConfigurationFactory.getInstance().getConfiguration().getOptionalValue(name, type).orElseGet(dft);
}
return AccessController.doPrivileged((PrivilegedAction<Path>) () -> {
final Optional<String> value = ConfigurationFactory.getInstance().getConfiguration().getOptionalValue("java.io.tmpdir", String.class);
return value.map(Path::of).orElseGet(() -> Path.of(System.getProperty("java.io.tmpdir")));
});
return AccessController.doPrivileged((PrivilegedAction<T>) () -> ConfigurationFactory.getInstance()
.getConfiguration().getOptionalValue(name, type).orElseGet(dft));
}

}
Expand Up @@ -2,6 +2,8 @@

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;

/**
Expand Down Expand Up @@ -34,7 +36,30 @@ public Object extractEntity(ClientContext context, Object... args)
{
Class<?>[] intfs = {returnType};
ClientResponseProxy clientProxy = new ClientResponseProxy(context, methodHandlers, returnType);
return Proxy.newProxyInstance(returnType.getClassLoader(), intfs, clientProxy);
ClassLoader clazzLoader;
final SecurityManager sm = System.getSecurityManager();
if (sm == null) {
clazzLoader = returnType.getClassLoader();
// The class loader may be null for primitives, void or the type was loaded from the bootstrap class loader.
// In such cases we should use the TCCL.
if (clazzLoader == null) {
clazzLoader = Thread.currentThread().getContextClassLoader();
}
} else {
clazzLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
ClassLoader result = returnType.getClassLoader();
// The class loader may be null for primitives, void or the type was loaded from the bootstrap class loader.
// In such cases we should use the TCCL.
if (result == null) {
result = Thread.currentThread().getContextClassLoader();
}
return result;
}
});
}
return Proxy.newProxyInstance(clazzLoader, intfs, clientProxy);
}

}

0 comments on commit c9a75d2

Please sign in to comment.