Skip to content
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
@@ -0,0 +1,12 @@
/*
* Copyright 2024 Red Hat, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package org.jboss.pnc.reqour.common.exceptions;

public class ConflictingInternalUrlException extends RuntimeException {

public ConflictingInternalUrlException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2024 Red Hat, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package org.jboss.pnc.reqour.common.utils;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.jboss.pnc.reqour.common.exceptions.ConflictingInternalUrlException;
import org.jboss.pnc.reqour.config.ConfigUtils;
import org.jboss.pnc.reqour.config.ReqourCoreConfig;

import lombok.NonNull;

@ApplicationScoped
public class ValidationUtils {

@Inject
ReqourCoreConfig reqourCoreConfig;

@Inject
ConfigUtils configUtils;

public void validateInternalUrlMatchesActiveGitProvider(@NonNull String internalUrl) {
if (!reqourCoreConfig.git().validateInternalUrl()) {
return;
}

final String activeGitProvidersHostname = configUtils.getActiveGitProvidersHostname();
if (!internalUrl.contains(activeGitProvidersHostname)) {
throw new ConflictingInternalUrlException(
String.format(
"Provided internal URL (%s) is not from active git provider's hostname (%s)",
internalUrl,
activeGitProvidersHostname));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ConfigConstants {
public static final String GITLAB_INTERNAL_URL = REQOUR_CORE_CONFIG
+ ".git.git-providers.gitlab.git-url-internal-template";
public static final String PRIVATE_GITHUB_USER = REQOUR_CORE_CONFIG + ".git.private-github-user";
public static final String INTERNAL_URL_VALIDATION = REQOUR_CORE_CONFIG + ".git.validate-internal-url";
//endregion

//region Reqour Adjuster config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public GitProvider getActiveGitProvider() {
throw new IllegalArgumentException("No git provider is enabled");
}

public String getActiveGitProvidersHostname() {
return switch (getActiveGitProvider()) {
case GITLAB -> config.git().gitProviders().gitlab().hostname();
case GITHUB -> config.git().gitProviders().github().hostname();
};
}

public Optional<String> getPrivateGithubUser() {
return config.git().privateGithubUser();
}
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/jboss/pnc/reqour/config/GitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import java.util.Optional;
import java.util.Set;

import jakarta.ws.rs.DefaultValue;

/**
* Configuration of all git-related stuff, e.g. git providers and acceptable schemes.
*/
Expand All @@ -21,5 +23,14 @@ public interface GitConfig {

Optional<List<String>> internalUrls();

/**
* Boolean flag whether internal URLs provided in some requests should be validated towards active git provider's
* hostname.<br/>
* For instance, when GitHub@IBM is set as active git provider, requests with GitLab@RedHat as its internal URL
* should fail.
*/
@DefaultValue("true")
boolean validateInternalUrl();

Committer user();
}
1 change: 1 addition & 0 deletions core/src/main/resources/application-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ reqour:
- git
- git+ssh
- ssh
validate-internal-url: false
private-github-user: test-bot
user:
email: [email protected]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class TestDataSupplier {
public static final String CALLBACK_PATH = "/callback";
public static final String BIFROST_FINAL_LOG_UPLOAD_PATH = "/final-log/upload";
public static final String TASK_ID = "task-id";
public static final String PROCESS_CONTEXT = "my-process-context";

public static class Translation {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2024 Red Hat, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package org.jboss.pnc.reqour.common.profile;

import java.util.Map;

import org.jboss.pnc.reqour.config.ConfigConstants;

public class WithInternalUrlValidation extends CommonTestProfile {

@Override
public Map<String, String> getConfigOverrides() {
return Map.ofEntries(
Map.entry(ConfigConstants.INTERNAL_URL_VALIDATION, ConfigConstants.TRUE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.jboss.pnc.api.reqour.rest.AdjustEndpoint;
import org.jboss.pnc.common.http.PNCHttpClient;
import org.jboss.pnc.common.log.ProcessStageUtils;
import org.jboss.pnc.reqour.common.utils.ValidationUtils;
import org.jboss.pnc.reqour.enums.AdjustProcessStage;
import org.jboss.pnc.reqour.rest.openshift.OpenShiftAdjusterJobController;
import org.jboss.pnc.reqour.rest.service.FinalLogManager;
Expand All @@ -34,6 +35,7 @@ public class AdjustEndpointImpl implements AdjustEndpoint {
private final OpenShiftAdjusterJobController openShiftAdjusterJobController;
private final PNCHttpClient pncHttpClient;
private final Logger userLogger;
private final ValidationUtils validationUtils;
private final FinalLogManager finalLogManager;

@Inject
Expand All @@ -42,11 +44,13 @@ public AdjustEndpointImpl(
ManagedExecutor managedExecutor,
PNCHttpClient pncHttpClient,
@UserLogger Logger userLogger,
ValidationUtils validationUtils,
FinalLogManager finalLogManager) {
this.managedExecutor = managedExecutor;
this.pncHttpClient = pncHttpClient;
this.openShiftAdjusterJobController = openShiftAdjusterJobController;
this.userLogger = userLogger;
this.validationUtils = validationUtils;
this.finalLogManager = finalLogManager;
}

Expand All @@ -55,6 +59,8 @@ public AdjustEndpointImpl(
public void adjust(AdjustRequest adjustRequest) {
userLogger.info("Adjust request: {}", adjustRequest);

validationUtils.validateInternalUrlMatchesActiveGitProvider(adjustRequest.getInternalUrl().getReadwriteUrl());

managedExecutor.runAsync(() -> {
ProcessStageUtils.logProcessStageBegin(AdjustProcessStage.STARTING_ALIGNMENT_POD.name());
finalLogManager.addMessage(getMessageStepStartingAlignmentPod(ProcessStageUtils.Step.BEGIN));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.jboss.pnc.reqour.common.callbacksender.CallbackSender;
import org.jboss.pnc.reqour.common.exceptions.GitException;
import org.jboss.pnc.reqour.common.executor.task.TaskExecutor;
import org.jboss.pnc.reqour.common.utils.ValidationUtils;
import org.jboss.pnc.reqour.runtime.UserLogger;
import org.jboss.pnc.reqour.service.api.CloneService;
import org.slf4j.Logger;
Expand All @@ -31,17 +32,20 @@ public class CloneEndpointImpl implements CloneEndpoint {
private final CloneService service;
private final TaskExecutor taskExecutor;
private final CallbackSender callbackSender;
private final ValidationUtils validationUtils;
private final Logger userLogger;

@Inject
public CloneEndpointImpl(
CloneService service,
TaskExecutor taskExecutor,
CallbackSender callbackSender,
ValidationUtils validationUtils,
@UserLogger Logger logger) {
this.service = service;
this.taskExecutor = taskExecutor;
this.callbackSender = callbackSender;
this.validationUtils = validationUtils;
this.userLogger = logger;
}

Expand All @@ -50,6 +54,8 @@ public CloneEndpointImpl(
public void clone(RepositoryCloneRequest cloneRequest) {
userLogger.info("Clone request: {}", cloneRequest);

validationUtils.validateInternalUrlMatchesActiveGitProvider(cloneRequest.getTargetRepoUrl());

taskExecutor.executeAsync(
cloneRequest.getCallback(),
cloneRequest,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 Red Hat, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package org.jboss.pnc.reqour.rest.providers;

import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;

import org.jboss.pnc.api.dto.ErrorResponse;
import org.jboss.pnc.reqour.common.exceptions.ConflictingInternalUrlException;

import lombok.extern.slf4j.Slf4j;

@Provider
@Slf4j
public class ConflictingInternalUrlExceptionMapper implements ExceptionMapper<ConflictingInternalUrlException> {

@Override
public Response toResponse(ConflictingInternalUrlException exception) {
log.warn("Invalid internal URL provided", exception);
return Response.status(Response.Status.CONFLICT)
.entity(new ErrorResponse(exception))
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jboss.pnc.api.reqour.dto.ReqourCallback;
import org.jboss.pnc.api.reqour.rest.AdjustEndpoint;
import org.jboss.pnc.common.log.ProcessStageUtils;
import org.jboss.pnc.reqour.common.TestDataSupplier;
import org.jboss.pnc.reqour.common.TestUtils;
import org.jboss.pnc.reqour.rest.openshift.OpenShiftAdjusterJobController;
import org.jboss.pnc.reqour.rest.service.FinalLogManager;
Expand Down Expand Up @@ -50,7 +51,6 @@
@TestSecurity(user = TEST_USER, roles = { OidcRoleConstants.PNC_APP_REPOUR_USER })
class AdjustEndpointImplTest {

private static final String PROCESS_CONTEXT = "my-process-context";
private static final String EXCEPTION_MESSAGE = "Ooops, something went terribly wrongie";

WireMock wireMock;
Expand Down Expand Up @@ -83,7 +83,7 @@ void adjust_adjusterJobCreationSuccessed_sendsFinalLogToBifrost() throws Interru

Response response = RestAssured.given()
.contentType(MediaType.APPLICATION_JSON)
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), PROCESS_CONTEXT))
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), TestDataSupplier.PROCESS_CONTEXT))
.body(TestUtils.createAdjustRequest())
.when()
.post();
Expand Down Expand Up @@ -113,7 +113,7 @@ void adjust_adjusterJobCreationFailed_sendsFinalLogToBifrost()

Response response = RestAssured.given()
.contentType(MediaType.APPLICATION_JSON)
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), PROCESS_CONTEXT))
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), TestDataSupplier.PROCESS_CONTEXT))
.body(TestUtils.createAdjustRequest())
.when()
.post();
Expand Down Expand Up @@ -156,7 +156,7 @@ void adjust_finalLogSendingFails_podIsDestroyedAndErrorLog()

Response response = RestAssured.given()
.contentType(MediaType.APPLICATION_JSON)
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), PROCESS_CONTEXT))
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), TestDataSupplier.PROCESS_CONTEXT))
.body(TestUtils.createAdjustRequest())
.when()
.post();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2024 Red Hat, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package org.jboss.pnc.reqour.rest.endpoints;

import static org.assertj.core.api.Assertions.assertThat;
import static org.jboss.pnc.reqour.rest.endpoints.TestConstants.TEST_USER;

import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response.Status;

import org.jboss.pnc.api.constants.MDCHeaderKeys;
import org.jboss.pnc.api.dto.Request;
import org.jboss.pnc.api.enums.BuildType;
import org.jboss.pnc.api.reqour.dto.AdjustRequest;
import org.jboss.pnc.api.reqour.dto.InternalGitRepositoryUrl;
import org.jboss.pnc.api.reqour.dto.RepositoryCloneRequest;
import org.jboss.pnc.reqour.common.TestDataSupplier;
import org.jboss.pnc.reqour.common.profile.WithInternalUrlValidation;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.quarkus.test.security.TestSecurity;
import io.restassured.RestAssured;
import io.restassured.http.Header;
import io.restassured.response.Response;

@QuarkusTest
@TestProfile(WithInternalUrlValidation.class)
@TestSecurity(user = TEST_USER, roles = { OidcRoleConstants.PNC_APP_REPOUR_USER })
public class EndpointValidationTest {

@Test
void adjust_invalidInternalUrl_conflictStatusReturned() {
Response response = RestAssured.given()
.basePath("/adjust")
.contentType(MediaType.APPLICATION_JSON)
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), TestDataSupplier.PROCESS_CONTEXT))
.body(
AdjustRequest.builder()
.taskId(TestDataSupplier.TASK_ID)
.callback(Request.builder().build())
.buildType(BuildType.MVN)
.internalUrl(
InternalGitRepositoryUrl.builder()
.readonlyUrl("https://github.invalid.internal.url.com/foo/bar.git")
.readwriteUrl("[email protected]:foo/bar.git")
.build())
.build())
.when()
.post();

assertThat(response.statusCode()).isEqualTo(Status.CONFLICT.getStatusCode());
}

@Test
void clone_invalidInternalUrl_conflictStatusReturned() {
Response response = RestAssured.given()
.basePath("/clone")
.contentType(MediaType.APPLICATION_JSON)
.header(new Header(MDCHeaderKeys.PROCESS_CONTEXT.getHeaderName(), TestDataSupplier.PROCESS_CONTEXT))
.body(
RepositoryCloneRequest.builder()
.taskId(TestDataSupplier.TASK_ID)
.callback(Request.builder().build())
.originRepoUrl("https://github.com/foo/bar.git")
.targetRepoUrl("[email protected]:foo/bar.git")
.build())
.when()
.post();

assertThat(response.statusCode()).isEqualTo(Status.CONFLICT.getStatusCode());
}
}