Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENG-5464: Fix CDS internal section bug #260

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class CdsConfiguration {
private String cdsPublicUrl;
@Value("${CDS_PUBLIC_PATH:}")
private String cdsPublicPath;
@Value("${CDS_INTERNAL_PUBLIC_SECTION:}")
private String cdsInternalPublicSection;
@Value("${CDS_PRIVATE_URL:https://cds.entando.org}")
private String cdsPrivateUrl;
@Value("${CDS_PATH:/api/v1}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ private void create(String subPath, boolean isProtectedResource, Optional<InputS
if(StringUtils.isBlank(subPath)){
throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG);
}

this.validateAndReturnResourcePath(config, subPath, isProtectedResource);

this.validateAndReturnResourcePath(config, subPath, false, isProtectedResource);
URI apiUrl = CdsUrlUtils.buildCdsInternalApiUrl(config, configuration, "/upload/");
CdsCreateResponseDto response = caller.executePostCall(apiUrl,
subPath,
Expand Down Expand Up @@ -118,18 +116,14 @@ public boolean deleteFile(String subPath, boolean isProtectedResource) {
if(StringUtils.isBlank(subPath)){
throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG);
}

this.validateAndReturnResourcePath(config, subPath, isProtectedResource);

this.validateAndReturnResourcePath(config, subPath, true, isProtectedResource);
URI apiUrl = EntUrlBuilder.builder()
.url(CdsUrlUtils.buildCdsInternalApiUrl(config, configuration))
.path("/delete/")
.path(CdsUrlUtils.getInternalSection(isProtectedResource, config, this.configuration))
.path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true))
.path(subPath)
.build();

return caller.executeDeleteCall(apiUrl, config, false);

} catch (EntRuntimeException ert) {
throw ert;
} catch (Exception e) {
Expand All @@ -147,15 +141,15 @@ public InputStream getStream(String subPath, boolean isProtectedResource) throws
throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG);
}

this.validateAndReturnResourcePath(config, subPath, isProtectedResource);
this.validateAndReturnResourcePath(config, subPath, true, isProtectedResource);

url = (isProtectedResource) ?
CdsUrlUtils.buildCdsInternalApiUrl(config, configuration) :
CdsUrlUtils.buildCdsExternalPublicResourceUrl(config, configuration);

url = EntUrlBuilder.builder()
.url(url)
.path(CdsUrlUtils.getInternalSection(isProtectedResource, config, this.configuration))
.path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true))
.path(subPath).build();

Optional<ByteArrayInputStream> is = caller.getFile(url, config, isProtectedResource);
Expand All @@ -178,7 +172,7 @@ public InputStream getStream(String subPath, boolean isProtectedResource) throws
public String getResourceUrl(String subPath, boolean isProtectedResource) {
try {
Optional<TenantConfig> config = getTenantConfig();
return this.validateAndReturnResourcePath(config, subPath, isProtectedResource);
return this.validateAndReturnResourcePath(config, subPath, false, isProtectedResource);
} catch (Exception e) {
throw new EntRuntimeException("Error extracting resource url", e);
}
Expand Down Expand Up @@ -248,12 +242,12 @@ public BasicFileAttributeView[] listFileAttributes(String subPath, boolean isPro

private List<BasicFileAttributeView> listAttributes(String subPath, boolean isProtectedResource, CdsFilter filter) {
Optional<TenantConfig> config = this.getTenantConfig();
this.validateAndReturnResourcePath(config, subPath, isProtectedResource);
this.validateAndReturnResourcePath(config, subPath, true, isProtectedResource);

URI apiUrl = EntUrlBuilder.builder()
.url(CdsUrlUtils.buildCdsInternalApiUrl(config, configuration).toString())
.path("/list/")
.path(CdsUrlUtils.getInternalSection(isProtectedResource, config, this.configuration))
.path(CdsUrlUtils.getSection(isProtectedResource, config, this.configuration, true))
.path(subPath)
.build();

Expand Down Expand Up @@ -318,22 +312,19 @@ private Optional<TenantConfig> getTenantConfig() {
}


private String validateAndReturnResourcePath(Optional<TenantConfig> config, String resourceRelativePath, boolean privateUrl) {
private String validateAndReturnResourcePath(Optional<TenantConfig> config, String resourceRelativePath, boolean privateCall, boolean privateUrl) {
try {
String baseUrl = EntUrlBuilder.builder()
.url(CdsUrlUtils.fetchBaseUrl(config, configuration, privateUrl))
.path(CdsUrlUtils.getInternalSection(privateUrl, config, this.configuration)) // << this is part of base url because we want check path traversal!!
.path(CdsUrlUtils.getSection(privateUrl, config, this.configuration, privateCall))
.build().toString();

String fullPath = EntUrlBuilder.builder()
.url(baseUrl)
.path(resourceRelativePath)
.build().toString();

if (!StorageManagerUtil.doesPathContainsPath(baseUrl, fullPath, true)) {
throw mkPathValidationErr(baseUrl, fullPath);
}

return fullPath;
} catch (IOException e) {
throw new EntRuntimeException(ERROR_VALIDATING_PATH_MSG, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,33 @@ public final class CdsUrlUtils {
private static final String CDS_PUBLIC_URL_TENANT_PARAM = "cdsPublicUrl";
private static final String CDS_PRIVATE_URL_TENANT_PARAM = "cdsPrivateUrl";
private static final String CDS_PUBLIC_PATH_TENANT_PARAM = "cdsPublicPath";
private static final String CDS_INTERNAL_PUBLIC_SECTION_TENANT_PARAM = "cdsInternalPublicSection";
private static final String CDS_PATH_TENANT_PARAM = "cdsPath";
private static final String URL_SEP = "/";
private static final String DEFAULT_SECTION_PUBLIC = "";
private static final String DEFAULT_SECTION_PRIVATE = "/protected";

private CdsUrlUtils(){
}

public static String getInternalSection(boolean isProtectedResource, Optional<TenantConfig> config, CdsConfiguration configuration) {
public static String getSection(boolean isProtectedResource, Optional<TenantConfig> config, CdsConfiguration configuration, boolean internalCall) {
if (isProtectedResource) {
return DEFAULT_SECTION_PRIVATE;
}
return config.map(c -> c.getProperty(CDS_PUBLIC_PATH_TENANT_PARAM).orElse(DEFAULT_SECTION_PUBLIC)).orElse(configuration.getCdsPublicPath());
if (internalCall) {
return config.map(c -> c.getProperty(CDS_INTERNAL_PUBLIC_SECTION_TENANT_PARAM).orElse(DEFAULT_SECTION_PUBLIC)).orElse(configuration.getCdsInternalPublicSection());
} else {
return config.map(c -> c.getProperty(CDS_PUBLIC_PATH_TENANT_PARAM).orElse(DEFAULT_SECTION_PUBLIC)).orElse(configuration.getCdsPublicPath());
}
}

public static URI buildCdsExternalPublicResourceUrl(Optional<TenantConfig> config, CdsConfiguration configuration, String ... paths){
log.debug("Trying to build CDS external public url with is tenant config empty:'{}', CDS primary configuration public url:'{}' and paths:'{}'",
config.isEmpty(),
configuration.getCdsPublicUrl(),
paths);

String publicUrl = config.flatMap(c -> c.getProperty(CDS_PUBLIC_URL_TENANT_PARAM)).orElse(configuration.getCdsPublicUrl());

return EntUrlBuilder.builder().url(publicUrl).paths(paths).build();

}

public static URI buildCdsInternalApiUrl(Optional<TenantConfig> config, CdsConfiguration configuration, String ... paths){
Expand All @@ -63,10 +65,8 @@ public static URI buildCdsInternalApiUrl(Optional<TenantConfig> config, CdsConfi
configuration.getCdsPrivateUrl(),
configuration.getCdsPath(),
paths);

String apiUrl = config.flatMap(c -> c.getProperty(CDS_PRIVATE_URL_TENANT_PARAM)).orElse(configuration.getCdsPrivateUrl());
String basePath = config.flatMap(c -> c.getProperty(CDS_PATH_TENANT_PARAM)).orElse(configuration.getCdsPath());

return EntUrlBuilder.builder().url(apiUrl).path(basePath).paths(paths).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ void shouldManageErrorWhenCallGetStream() throws Exception {
Map<String,String> configMap = Map.of("cdsPublicUrl", baseUrl,
"cdsPrivateUrl","http://cds-kube-service:8081/",
"cdsPublicPath","/custom-path",
"cdsInternalPublicSection", "/custom-path",
"cdsPath","/mytenant/api/v1/");
TenantConfig tc = new TenantConfig(configMap);
Mockito.when(tenantManager.getConfig("my-tenant")).thenReturn(Optional.ofNullable(tc));
Expand All @@ -244,8 +245,7 @@ void shouldManageErrorWhenCallGetStream() throws Exception {
Assertions.assertThatThrownBy(
()-> cdsStorageManager.getStream(testFilePath,false)
).isInstanceOf(EntException.class).hasMessageStartingWith("Error extracting file");



String testFilePathBadGateway = "/testfolder/test-badgw.txt";
URI testFileBadGateway = URI.create( baseUrl + "/custom-path" + testFilePathBadGateway);
Mockito.when(cdsRemoteCaller.getFile(eq(testFileBadGateway),
Expand Down Expand Up @@ -366,6 +366,7 @@ void shouldWorkFineWhenCallExistsWithRootAsEmpty() throws Exception {
Map<String,String> configMap = Map.of("cdsPublicUrl","http://my-server/tenant1/cms-resources",
"cdsPrivateUrl","http://cds-tenant1-kube-service:8081/",
"cdsPublicPath","/public",
"cdsInternalPublicSection", "/public",
"cdsPath","/mytenant/api/v1/");
TenantConfig tc = new TenantConfig(configMap);
Mockito.when(tenantManager.getConfig("my-tenant")).thenReturn(Optional.ofNullable(tc));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class CdsUtilsTest {
void shouldExtractPathAndFilename() throws Exception {
EntSubPath subPath = CdsUrlUtils.extractPathAndFilename("/folder1/folder2/file.txt");
Assertions.assertEquals("file.txt", subPath.getFileName());
//Assertions.assertEquals("folder1/folder2/", subPath.getPath());
Assertions.assertEquals("/folder1/folder2", subPath.getPath());

subPath = CdsUrlUtils.extractPathAndFilename("file.txt");
Expand All @@ -40,12 +39,10 @@ void shouldExtractPathAndFilename() throws Exception {

subPath = CdsUrlUtils.extractPathAndFilename("/folder/");
Assertions.assertEquals("", subPath.getFileName());
//Assertions.assertEquals("folder/", subPath.getPath());
Assertions.assertEquals("/folder", subPath.getPath());

subPath = CdsUrlUtils.extractPathAndFilename("../../folder/file.txt");
Assertions.assertEquals("file.txt", subPath.getFileName());
//Assertions.assertEquals("../../folder/", subPath.getPath());
Assertions.assertEquals("../../folder", subPath.getPath());

subPath = CdsUrlUtils.extractPathAndFilename("");
Expand All @@ -66,23 +63,23 @@ void shouldWorkFineWithToString() {

@Test
void shouldExtractRigthSection() {
Assertions.assertEquals("/protected", CdsUrlUtils.getInternalSection(true, null, null));
Assertions.assertEquals("/protected", CdsUrlUtils.getInternalSection(true, null, Mockito.mock(CdsConfiguration.class)));
Assertions.assertEquals("/protected", CdsUrlUtils.getSection(true, null, null, true));
Assertions.assertEquals("/protected", CdsUrlUtils.getSection(true, null, Mockito.mock(CdsConfiguration.class), true));

CdsConfiguration cdsConfiguration = new CdsConfiguration();
cdsConfiguration.setCdsPublicPath("");
Assertions.assertEquals("", CdsUrlUtils.getInternalSection(false, Optional.ofNullable(null), cdsConfiguration));
Assertions.assertEquals("", CdsUrlUtils.getSection(false, Optional.ofNullable(null), cdsConfiguration, false));

cdsConfiguration.setCdsPublicPath("/public");
Assertions.assertEquals("/public", CdsUrlUtils.getInternalSection(false, Optional.ofNullable(null), cdsConfiguration));
Assertions.assertEquals("/public", CdsUrlUtils.getSection(false, Optional.ofNullable(null), cdsConfiguration, false));

Map<String, String> tenantParams = new HashMap<>();
TenantConfig tenantConfig = new TenantConfig(tenantParams);
Assertions.assertEquals("", CdsUrlUtils.getInternalSection(false, Optional.ofNullable(tenantConfig), cdsConfiguration));
Assertions.assertEquals("", CdsUrlUtils.getSection(false, Optional.ofNullable(tenantConfig), cdsConfiguration, true));

tenantParams.put("cdsPublicPath", "/customPath");
Assertions.assertEquals("/customPath", CdsUrlUtils.getInternalSection(false, Optional.ofNullable(tenantConfig), cdsConfiguration));
Assertions.assertEquals("/protected", CdsUrlUtils.getInternalSection(true, Optional.ofNullable(tenantConfig), cdsConfiguration));
Assertions.assertEquals("/customPath", CdsUrlUtils.getSection(false, Optional.ofNullable(tenantConfig), cdsConfiguration, false));
Assertions.assertEquals("/protected", CdsUrlUtils.getSection(true, Optional.ofNullable(tenantConfig), cdsConfiguration, true));
}

}
Loading