Skip to content
Snippets Groups Projects
Commit 45c17bc2 authored by Martin Lowe's avatar Martin Lowe :flag_ca:
Browse files

Merge branch 'malowe/main/75' into 'main'

Iss #75 - Add authentication protection to the user lookup endpoint

See merge request eclipsefdn/it/api/git-eca-rest-api!184
parents aa461607 d472d0a1
No related branches found
No related tags found
No related merge requests found
Showing
with 357 additions and 243 deletions
......@@ -55,4 +55,15 @@ CREATE TABLE GithubWebhookTracking (
needsRevalidation tinyint(1) DEFAULT 0,
manualRevalidationCount int DEFAULT 0,
PRIMARY KEY (id)
);
\ No newline at end of file
);
-- eclipse_eca.GithubApplicationInstallation definition
CREATE TABLE `GithubApplicationInstallation` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`appId` int(11) NOT NULL,
`installationId` int(11) NOT NULL,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`lastUpdated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=389 DEFAULT CHARSET=utf8;
\ No newline at end of file
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipsefoundation</groupId>
<artifactId>git-eca</artifactId>
<version>1.1.0</version>
<properties>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.2.11.Final</quarkus.platform.version>
<surefire-plugin.version>3.1.2</surefire-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<eclipse-api-version>0.9.5</eclipse-api-version>
<auto-value.version>1.10.4</auto-value.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<sonar.sources>src/main</sonar.sources>
<sonar.tests>src/test</sonar.tests>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.coverage.jacoco.xmlReportPaths>
${project.basedir}/target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
<sonar.junit.reportPath>${project.build.directory}/surefire-reports</sonar.junit.reportPath>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>eclipse-foundation-it</sonar.organization>
<sonar.projectKey>git-eca-rest-api</sonar.projectKey>
<sonar.projectName>Git ECA REST API</sonar.projectName>
</properties>
<repositories>
<repository>
<id>eclipsefdn</id>
<url>https://repo.eclipse.org/content/repositories/eclipsefdn/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencyManagement>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipsefoundation</groupId>
<artifactId>git-eca</artifactId>
<version>1.1.0</version>
<properties>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.2.11.Final</quarkus.platform.version>
<surefire-plugin.version>3.1.2</surefire-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<eclipse-api-version>0.9.5</eclipse-api-version>
<auto-value.version>1.10.4</auto-value.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<sonar.sources>src/main</sonar.sources>
<sonar.tests>src/test</sonar.tests>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.coverage.jacoco.xmlReportPaths>
${project.basedir}/target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
<sonar.junit.reportPath>${project.build.directory}/surefire-reports</sonar.junit.reportPath>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>eclipse-foundation-it</sonar.organization>
<sonar.projectKey>git-eca-rest-api</sonar.projectKey>
<sonar.projectName>Git ECA REST API</sonar.projectName>
</properties>
<repositories>
<repository>
<id>eclipsefdn</id>
<url>https://repo.eclipse.org/content/repositories/eclipsefdn/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-core</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-persistence</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-efservices</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-context-propagation</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-core</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-persistence</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-efservices</artifactId>
<version>${eclipse-api-version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-context-propagation</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<!-- Required for PKCS1 compatibility -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
<!-- Required for PKCS1 compatibility -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
<!-- Annotation preprocessors - reduce all of the boiler plate -->
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<!-- Annotation preprocessors - reduce all of the boiler plate -->
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<!-- Test requirements -->
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-test-common</artifactId>
<version>${eclipse-api-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope>
</dependency>
<!-- Test requirements -->
<dependency>
<groupId>org.eclipsefoundation</groupId>
<artifactId>quarkus-test-common</artifactId>
<version>${eclipse-api-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope>
</dependency>
<!-- Following H2/devservices deps are made to circumvent need for docker -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<!-- Flyway specific dependencies, used to setup tables in test -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Following H2/devservices deps are made to circumvent need for
docker -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<!-- Flyway specific dependencies, used to setup tables in test -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security-oidc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<skipTests>false</skipTests>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>${auto-value.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<skipTests>false</skipTests>
<systemPropertyVariables>
<java.util.logging.manager>
org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
......@@ -13,23 +13,29 @@ package org.eclipsefoundation.git.eca.resource;
import java.util.Arrays;
import jakarta.inject.Inject;
import org.eclipsefoundation.core.helper.DateTimeHelper;
import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.git.eca.api.models.EclipseUser;
import org.eclipsefoundation.git.eca.dto.GithubWebhookTracking;
import org.eclipsefoundation.git.eca.service.UserService;
import org.eclipsefoundation.persistence.dao.PersistenceDao;
import org.eclipsefoundation.persistence.model.RDBMSQuery;
import org.eclipsefoundation.persistence.service.FilterService;
import io.quarkus.security.identity.SecurityIdentity;
import io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipal;
import jakarta.inject.Inject;
/**
* Contains operations and properties that are common to resources that interact with Github validation.
* Contains operations and properties that are common to resources that interact with Github validation and user accounts.
*
* @author Martin Lowe
*
*/
public abstract class GithubAdjacentResource {
public abstract class CommonResource {
@Inject
UserService users;
@Inject
PersistenceDao dao;
@Inject
......@@ -37,10 +43,22 @@ public abstract class GithubAdjacentResource {
@Inject
RequestWrapper wrapper;
@Inject
SecurityIdentity ident;
void setRevalidationFlagForTracking(GithubWebhookTracking tracking) {
tracking.setNeedsRevalidation(true);
tracking.setLastUpdated(DateTimeHelper.now());
dao.add(new RDBMSQuery<>(wrapper, filters.get(GithubWebhookTracking.class)), Arrays.asList(tracking));
}
EclipseUser getUserForLoggedInAccount() {
if (ident.isAnonymous()) {
return null;
}
// cast to a principal w/ access to claims
DefaultJWTCallerPrincipal defaultPrin = (DefaultJWTCallerPrincipal) ident.getPrincipal();
// get the user account linked to the current claim
return users.getUser(defaultPrin.getClaim("email"));
}
}
......@@ -53,7 +53,7 @@ import jakarta.ws.rs.core.Response.Status;
*
*/
@Path("webhooks/github")
public class GithubWebhooksResource extends GithubAdjacentResource {
public class GithubWebhooksResource extends CommonResource {
private static final Logger LOGGER = LoggerFactory.getLogger(GithubWebhooksResource.class);
@Inject
......
package org.eclipsefoundation.git.eca.resource;
import java.net.URI;
import io.quarkus.security.Authenticated;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
/**
* Create a basic endpoint for enabling logins with redirects.
*/
@Authenticated
@Path("login")
public class OIDCResource {
@GET
public Response login(@QueryParam("redirect") URI redirect) {
return Response.status(302).location(redirect).build();
}
}
......@@ -38,6 +38,7 @@ import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
/**
* REST resource containing endpoints related to checking the status of validation requests.
......@@ -46,7 +47,7 @@ import jakarta.ws.rs.core.Response;
*
*/
@Path("eca/status")
public class StatusResource extends GithubAdjacentResource {
public class StatusResource extends CommonResource {
// parameter names for the status error page
private static final String INCLUDE_INSTALL_LINK_PARAMETER = "includeInstallLink";
......@@ -66,6 +67,9 @@ public class StatusResource extends GithubAdjacentResource {
@Inject
ProjectHelper projects;
@Inject
UriInfo info;
// Qute templates, generates UI status page
@Location("simple_fingerprint_ui")
Template statusUiTemplate;
......@@ -85,8 +89,7 @@ public class StatusResource extends GithubAdjacentResource {
}
/**
* Retrieves commit status information based on the fingerprint and builds a UI around the results for easier
* consumption.
* Retrieves commit status information based on the fingerprint and builds a UI around the results for easier consumption.
*
* @param fingerprint the string associated with the request for looking up related commit statuses.
* @return the HTML UI for the status of the fingerprint request
......@@ -109,13 +112,15 @@ public class StatusResource extends GithubAdjacentResource {
.data("project", ps.isEmpty() ? null : ps.get(0))
.data(REPO_URL_PARAMETER, statuses.get(0).getRepoUrl())
.data("installationId", null)
.data("currentUser", getUserForLoggedInAccount())
.data("redirectUri", info.getPath())
.render())
.build();
}
/**
* Retrieves and checks the validity of the commit statuses for a Github pull request, and if out of date will
* revalidate the request automatically on load.
* Retrieves and checks the validity of the commit statuses for a Github pull request, and if out of date will revalidate the request
* automatically on load.
*
* @param org the organization in Github that contains the target repo
* @param repoName the name of the repo in Github containing the pull request
......@@ -150,8 +155,7 @@ public class StatusResource extends GithubAdjacentResource {
// get the data about the current request, and check that we have some validation statuses
ValidationRequest req = validationHelper.generateRequest(installationId, repoFullName, prNo, repoUrl);
List<CommitValidationStatus> statuses = validationStatus
.getHistoricValidationStatusByShas(wrapper,
req.getCommits().stream().map(Commit::getHash).toList());
.getHistoricValidationStatusByShas(wrapper, req.getCommits().stream().map(Commit::getHash).toList());
// check if we have any data, and if there is none, attempt to validate the request information
if (statuses.isEmpty()) {
// run the validation for the current request adhoc
......@@ -170,8 +174,7 @@ public class StatusResource extends GithubAdjacentResource {
}
// retrieve the status of the commits to display on the status page
statuses = validationStatus
.getHistoricValidationStatusByShas(wrapper, r.getCommits().keySet().stream().toList());
statuses = validationStatus.getHistoricValidationStatusByShas(wrapper, r.getCommits().keySet().stream().toList());
}
// get projects for use in queries + UI
......@@ -186,6 +189,8 @@ public class StatusResource extends GithubAdjacentResource {
.data("project", ps.isEmpty() ? null : ps.get(0))
.data(REPO_URL_PARAMETER, repoUrl)
.data("installationId", installationId)
.data("currentUser", getUserForLoggedInAccount())
.data("redirectUri", info.getPath())
.render())
.build();
} catch (BadRequestException e) {
......@@ -212,4 +217,5 @@ public class StatusResource extends GithubAdjacentResource {
.build();
}
}
}
......@@ -16,67 +16,60 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.eclipsefoundation.core.exception.FinalForbiddenException;
import org.eclipsefoundation.core.helper.TransformationHelper;
import org.eclipsefoundation.core.model.RequestWrapper;
import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.git.eca.api.models.EclipseUser;
import org.eclipsefoundation.git.eca.helper.ProjectHelper;
import org.eclipsefoundation.git.eca.model.ValidationRequest;
import org.eclipsefoundation.git.eca.model.ValidationResponse;
import org.eclipsefoundation.git.eca.namespace.APIStatusCode;
import org.eclipsefoundation.git.eca.service.UserService;
import org.eclipsefoundation.git.eca.service.ValidationService;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* ECA validation endpoint for Git commits. Will use information from the bots, projects, and accounts API to validate
* commits passed to this endpoint. Should be as system agnostic as possible to allow for any service to request
* validation with less reliance on services external to the Eclipse foundation.
* ECA validation endpoint for Git commits. Will use information from the bots, projects, and accounts API to validate commits passed to
* this endpoint. Should be as system agnostic as possible to allow for any service to request validation with less reliance on services
* external to the Eclipse foundation.
*
* @author Martin Lowe, Zachary Sabourin
*/
@Path("/eca")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public class ValidationResource {
public class ValidationResource extends CommonResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ValidationResource.class);
@Inject
RequestWrapper wrapper;
// external API/service harnesses
@Inject
CachingService cache;
@Inject
ProjectHelper projects;
@Inject
UserService users;
@Inject
ValidationService validation;
/**
* Consuming a JSON request, this method will validate all passed commits, using the repo URL and the repository
* provider. These commits will be validated to ensure that all users are covered either by an ECA, or are committers on
* the project. In the case of ECA-only contributors, an additional sign off footer is required in the body of the
* commit.
* Consuming a JSON request, this method will validate all passed commits, using the repo URL and the repository provider. These commits
* will be validated to ensure that all users are covered either by an ECA, or are committers on the project. In the case of ECA-only
* contributors, an additional sign off footer is required in the body of the commit.
*
* @param req the request containing basic data plus the commits to be validated
* @return a web response indicating success or failure for each commit, along with standard messages that may be used
* to give users context on failure.
* @return a web response indicating success or failure for each commit, along with standard messages that may be used to give users
* context on failure.
*/
@POST
public Response validate(ValidationRequest req) {
......@@ -95,7 +88,14 @@ public class ValidationResource {
@GET
@Path("/lookup")
@Authenticated
public Response getUserStatus(@QueryParam("email") String email) {
// check that the user has committer level access
EclipseUser loggedInUser = getUserForLoggedInAccount();
if (loggedInUser == null || !loggedInUser.getIsCommitter()) {
throw new FinalForbiddenException("User must be logged in and have committer level access to use this endpoint.");
}
// do the lookup of the passed email
EclipseUser user = users.getUser(email);
if (Objects.isNull(user)) {
throw new NotFoundException(String.format("No user found with mail '%s'", TransformationHelper.formatLog(email)));
......@@ -108,8 +108,7 @@ public class ValidationResource {
}
/**
* Check if there are any issues with the validation request, returning error messages if there are issues with the
* request.
* Check if there are any issues with the validation request, returning error messages if there are issues with the request.
*
* @param req the current validation request
* @return a list of error messages to report, or an empty list if there are no errors with the request.
......
......@@ -21,8 +21,11 @@ quarkus.hibernate-orm.packages=org.eclipsefoundation.git.eca.dto
quarkus.hibernate-orm.datasource=<default>
## Security configs
quarkus.oauth2.enabled=false
quarkus.oidc.enabled=false
quarkus.oidc.application-type=web-app
quarkus.oidc.token.refresh-expired=true
quarkus.oidc.authentication.session-age-extension=60m
quarkus.oidc.discovery-enabled=true
quarkus.oidc.roles.source=accesstoken
eclipse.security.oauth2.token-generation.scope=eclipsefdn_view_all_profiles
eclipse.security.oauth2.token-generation.client-id=placeholder
eclipse.security.oauth2.token-generation.client-secret=placeholder
......
......@@ -179,6 +179,7 @@
<section id="block-site-login-eclipse-eca-sle-eca-lookup-tool"
class="margin-bottom-30 clearfix">
<h2>ECA Validation Tool</h2>
{#if currentUser and currentUser.isCommitter}
<form id="eclipse-eca-lookup-form" accept-charset="UTF-8">
<div class="form-item form-item-input form-type-textfield form-group">
<input placeholder="Enter email address" class="form-control form-text" type="text" id="email-input"
......@@ -187,6 +188,12 @@
</div>
<button class="btn-success btn form-submit" type="submit" id="edit-submit">Verify ECA</button>
</form>
{#else if currentUser}
<p>Logged in users must have committer level access to use the ECA Validation tool.</p>
{#else}
<p>Please login to use the ECA Validation tool. Please note that only committers are able to use this tool.</p>
<a class="btn btn-primary" href="/git/login?redirect={redirectUri}">Login</a>
{/if}
</section>
<section id="block-eclipse-api-github-eclipse-api-github-links" class="main-sidebar-default-margin">
<ul id="leftnav" class="ul-left-nav fa-ul hidden-print" role="tablist">
......
......@@ -23,8 +23,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import jakarta.inject.Inject;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.core.service.CachingService;
import org.eclipsefoundation.git.eca.model.Commit;
......@@ -33,8 +31,8 @@ import org.eclipsefoundation.git.eca.model.ValidationRequest;
import org.eclipsefoundation.git.eca.namespace.APIStatusCode;
import org.eclipsefoundation.git.eca.namespace.ProviderType;
import org.eclipsefoundation.git.eca.test.namespaces.SchemaNamespaceHelper;
import org.eclipsefoundation.testing.helpers.AuthHelper;
import org.eclipsefoundation.testing.helpers.TestCaseHelper;
import org.eclipsefoundation.testing.models.EndpointTestBuilder;
import org.eclipsefoundation.testing.models.EndpointTestCase;
import org.junit.jupiter.api.Assertions;
......@@ -45,10 +43,15 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.quarkus.test.security.oidc.Claim;
import io.quarkus.test.security.oidc.OidcSecurity;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response.Status;
/**
* Tests for verifying end to end validation via the endpoint. Uses restassured to create pseudo requests, and Mock API
* endpoints to ensure that all data is kept internal for test checks.
* Tests for verifying end to end validation via the endpoint. Uses restassured to create pseudo requests, and Mock API endpoints to ensure
* that all data is kept internal for test checks.
*
* @author Martin Lowe
* @author Zachary Sabourin
......@@ -115,6 +118,10 @@ class ValidationResourceTest {
*/
public static final EndpointTestCase LOOKUP_SUCCESS_CASE = TestCaseHelper
.buildSuccessCase(LOOKUP_URL, new String[] { "slom@eclipse-foundation.org" }, "");
public static final EndpointTestCase LOOKUP_ANONYMOUS_CASE = TestCaseHelper
.prepareTestCase(LOOKUP_URL, new String[] { "slom@eclipse-foundation.org" }, "")
.setStatusCode(Status.UNAUTHORIZED.getStatusCode())
.build();
public static final EndpointTestCase LOOKUP_FORBIDDEN_CASE = TestCaseHelper
.buildForbiddenCase(LOOKUP_URL, new String[] { "newbie@important.co" }, "");
public static final EndpointTestCase LOOKUP_NOT_FOUND_CASE = TestCaseHelper
......@@ -869,16 +876,34 @@ class ValidationResourceTest {
* USER LOOKUP TESTS
*/
@Test
void validateUserLookup_failure_anonymous() {
EndpointTestBuilder.from(LOOKUP_ANONYMOUS_CASE).run();
}
@Test
@TestSecurity(user = "newbieAnon", roles = AuthHelper.DEFAULT_ROLE)
@OidcSecurity(claims = { @Claim(key = "email", value = "newbie@important.co") })
void validateUserLookup_failure_nonCommitter() {
EndpointTestBuilder.from(LOOKUP_FORBIDDEN_CASE).run();
}
@Test
@TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
@OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
void validateUserLookup_userNotFound() {
EndpointTestBuilder.from(LOOKUP_NOT_FOUND_CASE).run();
}
@Test
@TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
@OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
void validateUserLookup_userNoECA() {
EndpointTestBuilder.from(LOOKUP_FORBIDDEN_CASE).run();
}
@Test
@TestSecurity(user = AuthHelper.TEST_USER_NAME, roles = AuthHelper.DEFAULT_ROLE)
@OidcSecurity(claims = { @Claim(key = "email", value = "opearson@important.co") })
void validateUserLookup_userSuccess() {
EndpointTestBuilder.from(LOOKUP_NOT_FOUND_CASE).run();
}
......
......@@ -26,8 +26,8 @@ import org.eclipsefoundation.git.eca.api.models.EclipseUser.ECA;
import io.quarkus.test.Mock;
/**
* Simple stub for accounts API. Allows for easy testing of users that don't really exist upstream, and so that we don't
* need a real auth token for data.
* Simple stub for accounts API. Allows for easy testing of users that don't really exist upstream, and so that we don't need a real auth
* token for data.
*
* @author Martin Lowe
*
......@@ -104,7 +104,18 @@ public class MockAccountsAPI implements AccountsAPI {
.setName("sumAnalyst")
.setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
.build());
users
.put("opearson@important.co",
EclipseUser
.builder()
.setIsCommitter(true)
.setUid(id++)
.setMail("opearson@important.co")
.setName("opearson")
.setECA(ECA.builder().setCanContributeSpecProject(false).setSigned(true).build())
.setGithubHandle("opearson")
.setIsCommitter(true)
.build());
}
@Override
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment