Skip to content

Commit

Permalink
Support loading of base64 encoded values from the Environment
Browse files Browse the repository at this point in the history
  • Loading branch information
scottfrederick committed Feb 29, 2024
1 parent b74d134 commit b7c9c84
Show file tree
Hide file tree
Showing 20 changed files with 458 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 the original author or authors.
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,12 +26,16 @@
import java.util.stream.Collectors;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaCall;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClass.Predicates;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.JavaParameter;
import com.tngtech.archunit.core.domain.properties.CanBeAnnotated;
import com.tngtech.archunit.core.domain.properties.HasName;
import com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With;
import com.tngtech.archunit.core.domain.properties.HasParameterTypes;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
Expand All @@ -58,11 +62,14 @@
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskAction;

import org.springframework.util.ResourceUtils;

/**
* {@link Task} that checks for architecture problems.
*
* @author Andy Wilkinson
* @author Yanming Zhou
* @author Scott Frederick
*/
public abstract class ArchitectureCheck extends DefaultTask {

Expand All @@ -75,7 +82,8 @@ public ArchitectureCheck() {
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(),
noClassesShouldCallStepVerifierStepVerifyComplete(),
noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList(),
noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding());
noClassesShouldCallURLEncoderWithStringEncoding(), noClassesShouldCallURLDecoderWithStringEncoding(),
noClassesShouldLoadResourcesUsingResourceUtils());
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
}

Expand Down Expand Up @@ -208,6 +216,18 @@ private ArchRule noClassesShouldCallURLDecoderWithStringEncoding() {
.because("java.net.URLDecoder.decode(String s, Charset charset) should be used instead");
}

private ArchRule noClassesShouldLoadResourcesUsingResourceUtils() {
return ArchRuleDefinition.noClasses()
.should()
.callMethodWhere(JavaCall.Predicates.target(With.owner(Predicates.type(ResourceUtils.class)))
.and(JavaCall.Predicates.target(HasName.Predicates.name("getURL")))
.and(JavaCall.Predicates.target(HasParameterTypes.Predicates.rawParameterTypes(String.class)))
.or(JavaCall.Predicates.target(With.owner(Predicates.type(ResourceUtils.class)))
.and(JavaCall.Predicates.target(HasName.Predicates.name("getFile")))
.and(JavaCall.Predicates.target(HasParameterTypes.Predicates.rawParameterTypes(String.class)))))
.because("org.springframework.boot.io.ApplicationResourceLoader should be used instead");
}

public void setClasses(FileCollection classes) {
this.classes = classes;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,6 +36,7 @@
* Tests for {@link ArchitectureCheck}.
*
* @author Andy Wilkinson
* @author Scott Frederick
*/
class ArchitectureCheckTests {

Expand Down Expand Up @@ -121,6 +122,22 @@ void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasNoParametersTaskSucceed
});
}

@Test
void whenClassLoadsResourceUsingResourceUtilsTaskFailsAndWritesReport() throws Exception {
prepareTask("resources/loads", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture);
assertThat(failureReport(architectureCheck)).isNotEmpty();
});
}

@Test
void whenClassUsesResourceUtilsWithoutLoadingResourcesTaskSucceedsAndWritesAnEmptyReport() throws Exception {
prepareTask("resources/noloads", (architectureCheck) -> {
architectureCheck.checkArchitecture();
assertThat(failureReport(architectureCheck)).isEmpty();
});
}

private void prepareTask(String classes, Callback<ArchitectureCheck> callback) throws Exception {
File projectDir = new File(this.temp, "project");
projectDir.mkdirs();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* 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
*
* https://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.springframework.boot.build.architecture.resources.loads;

import java.io.FileNotFoundException;

import org.springframework.util.ResourceUtils;

public class ResourceUtilsResourceLoader {

void getResource() throws FileNotFoundException {
ResourceUtils.getURL("gradle.properties");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* 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
*
* https://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.springframework.boot.build.architecture.resources.noloads;

import java.net.MalformedURLException;
import java.net.URL;

import org.springframework.util.ResourceUtils;

public class ResourceUtilsWithoutLoading {

void inspectResourceLocation() throws MalformedURLException {
ResourceUtils.isUrl("gradle.properties");
ResourceUtils.isFileURL(new URL("gradle.properties"));
"test".startsWith(ResourceUtils.FILE_URL_PREFIX);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,13 +16,12 @@

package org.springframework.boot.autoconfigure.ssl;

import java.io.FileNotFoundException;
import java.net.URL;
import java.nio.file.Path;

import org.springframework.boot.io.ApplicationResourceLoader;
import org.springframework.boot.ssl.pem.PemContent;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;

/**
Expand Down Expand Up @@ -57,23 +56,20 @@ Path toWatchPath() {

private Path toPath() {
try {
URL url = toUrl();
Assert.state(isFileUrl(url), () -> "Value '%s' is not a file URL".formatted(url));
return Path.of(url.toURI()).toAbsolutePath();
Resource resource = getResource();
Assert.state(resource.isFile(), () -> "Value '%s' is not a file resource".formatted(this.value));
return Path.of(resource.getFile().getAbsolutePath());
}
catch (Exception ex) {
throw new IllegalStateException("Unable to convert value of property '%s' to a path".formatted(this.name),
ex);
}
}

private URL toUrl() throws FileNotFoundException {
private Resource getResource() {
Assert.state(!isPemContent(), "Value contains PEM content");
return ResourceUtils.getURL(this.value);
}

private boolean isFileUrl(URL url) {
return "file".equalsIgnoreCase(url.getProtocol());
ApplicationResourceLoader resourceLoader = new ApplicationResourceLoader();
return resourceLoader.getResource(this.value);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,10 +16,11 @@

package org.springframework.boot.autoconfigure.ssl;

import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
Expand All @@ -37,9 +38,6 @@ class BundleContentPropertyTests {
-----END CERTIFICATE-----
""";

@TempDir
Path temp;

@Test
void isPemContentWhenValueIsPemTextReturnsTrue() {
BundleContentProperty property = new BundleContentProperty("name", PEM_TEXT);
Expand Down Expand Up @@ -78,8 +76,9 @@ void toWatchPathWhenNotPathThrowsException() {
}

@Test
void toWatchPathWhenPathReturnsPath() {
Path file = this.temp.toAbsolutePath().resolve("file.txt");
void toWatchPathWhenPathReturnsPath() throws URISyntaxException {
URL resource = getClass().getResource("keystore.jks");
Path file = Path.of(resource.toURI()).toAbsolutePath();
BundleContentProperty property = new BundleContentProperty("name", file.toString());
assertThat(property.toWatchPath()).isEqualTo(file);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,36 +19,26 @@
import java.io.File;
import java.io.IOException;

import org.springframework.boot.io.ApplicationResourceLoader;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ResourceUtils;

/**
* {@link Converter} to convert from a {@link String} to a {@link File}. Supports basic
* file conversion as well as file URLs.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class StringToFileConverter implements Converter<String, File> {

private static final ResourceLoader resourceLoader = new DefaultResourceLoader(null);
private static final ResourceLoader resourceLoader = new ApplicationResourceLoader();

@Override
public File convert(String source) {
if (ResourceUtils.isUrl(source)) {
return getFile(resourceLoader.getResource(source));
}
File file = new File(source);
if (file.exists()) {
return file;
}
Resource resource = resourceLoader.getResource(source);
if (resource.exists()) {
return getFile(resource);
}
return file;
return getFile(resource);
}

private File getFile(Resource resource) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* 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
*
* https://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.springframework.boot.io;

import org.springframework.core.io.ContextResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;

/**
* A {@link DefaultResourceLoader} with any {@link ProtocolResolver}s registered in a
* {@code spring.factories} file applied to it. Plain paths without a qualifier will
* resolve to file system resources. This is different from {@code DefaultResourceLoader},
* which resolves unqualified paths to classpath resources.
*
* @author Scott Frederick
* @since 3.3.0
*/
public class ApplicationResourceLoader extends DefaultResourceLoader {

/**
* Create a new {@code ApplicationResourceLoader}.
*/
public ApplicationResourceLoader() {
super();
ProtocolResolvers.applyTo(this);
}

/**
* Create a new {@code ApplicationResourceLoader}.
* @param classLoader the {@link ClassLoader} to load class path resources with, or
* {@code null} for using the thread context class loader at the time of actual
* resource access
*/
public ApplicationResourceLoader(ClassLoader classLoader) {
super(classLoader);
ProtocolResolvers.applyTo(this);
}

@Override
protected Resource getResourceByPath(String path) {
return new FileSystemContextResource(path);
}

private static class FileSystemContextResource extends FileSystemResource implements ContextResource {

FileSystemContextResource(String path) {
super(path);
}

@Override
public String getPathWithinContext() {
return getPath();
}

}

}

0 comments on commit b7c9c84

Please sign in to comment.