Skip to content

Commit

Permalink
[RHCLOUD-29487] Record gateway certificate details on database (RedHa…
Browse files Browse the repository at this point in the history
…tInsights#2366)

* [RHCLOUD-29487] Record gateway certificate details on database

---------

Co-authored-by: Gwenneg Lepage <[email protected]>
  • Loading branch information
g-duval and gwenneg authored Dec 1, 2023
1 parent 4f51385 commit a4a1f4c
Show file tree
Hide file tree
Showing 6 changed files with 407 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.redhat.cloud.notifications.db.repositories;

import com.redhat.cloud.notifications.models.Application;
import com.redhat.cloud.notifications.models.X509Certificate;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.transaction.Transactional;
import java.util.Optional;
import java.util.UUID;

@ApplicationScoped
public class X509CertificateRepository {

@Inject
EntityManager entityManager;

@Inject
ApplicationRepository applicationRepository;

@Transactional
public X509Certificate createCertificate(X509Certificate gatewayCertificate) {
Application application = applicationRepository.getApplication(gatewayCertificate.getBundle(), gatewayCertificate.getApplication());
gatewayCertificate.setCertificateApplication(application);
entityManager.persist(gatewayCertificate);
return gatewayCertificate;
}


public Optional<X509Certificate> findCertificate(String bundle, String application, String subjectDn) {
final String query = "SELECT gc FROM X509Certificate gc where gc.certificateApplication.bundle.name = :bundle " +
"AND gc.certificateApplication.name = :application " +
"AND gc.subjectDn = :subjectDn";
try {
return Optional.of(this.entityManager
.createQuery(query, X509Certificate.class)
.setParameter("bundle", bundle)
.setParameter("application", application)
.setParameter("subjectDn", subjectDn)
.getSingleResult());
} catch (NoResultException e) {
return Optional.empty();
}
}

@Transactional
public boolean updateCertificate(UUID id, X509Certificate gatewayCertificate) {
String hql = "UPDATE X509Certificate SET subjectDn = :subjectDn, sourceEnvironment = :sourceEnvironment WHERE id = :id";
int rowCount = entityManager.createQuery(hql)
.setParameter("subjectDn", gatewayCertificate.getSubjectDn())
.setParameter("sourceEnvironment", gatewayCertificate.getSourceEnvironment())
.setParameter("id", id)
.executeUpdate();
return rowCount > 0;
}

@Transactional
public boolean deleteCertificate(UUID id) {
String deleteHql = "DELETE FROM X509Certificate WHERE id = :id";
int rowCount = entityManager.createQuery(deleteHql)
.setParameter("id", id)
.executeUpdate();
return rowCount > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.redhat.cloud.notifications.models;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;

import static com.fasterxml.jackson.annotation.JsonProperty.Access.READ_ONLY;

@Entity
@Table(name = "x509_certificate")
@JsonNaming(SnakeCaseStrategy.class)
public class X509Certificate {
@Id
@GeneratedValue
@JsonProperty(access = READ_ONLY)
private UUID id;

@NotNull
private String subjectDn;

@NotNull
private String sourceEnvironment;

@NotNull
@Transient
private String bundle;

@NotNull
@Transient
private String application;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "application_id")
@JsonIgnore
private Application certificateApplication;

public UUID getId() {
return id;
}

public void setId(UUID id) {
this.id = id;
}

public String getSubjectDn() {
return subjectDn;
}

public void setSubjectDn(String subjectDn) {
this.subjectDn = subjectDn;
}

public String getSourceEnvironment() {
return sourceEnvironment;
}

public void setSourceEnvironment(String environment) {
this.sourceEnvironment = environment;
}

public String getBundle() {
return bundle;
}

public void setBundle(String bundle) {
this.bundle = bundle;
}

public String getApplication() {
return application;
}

public void setApplication(String application) {
this.application = application;
}

public Application getCertificateApplication() {
return certificateApplication;
}

public void setCertificateApplication(Application certificateApplication) {
this.certificateApplication = certificateApplication;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import com.redhat.cloud.event.parser.ConsoleCloudEventParser;
import com.redhat.cloud.event.parser.exceptions.ConsoleCloudEventValidationException;
import com.redhat.cloud.notifications.db.repositories.ApplicationRepository;
import com.redhat.cloud.notifications.db.repositories.X509CertificateRepository;
import com.redhat.cloud.notifications.ingress.Parser;
import com.redhat.cloud.notifications.ingress.ParsingException;
import com.redhat.cloud.notifications.models.EventType;
import com.redhat.cloud.notifications.models.X509Certificate;
import com.redhat.cloud.notifications.routers.internal.models.MessageValidationResponse;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
Expand All @@ -20,6 +23,7 @@
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.jboss.resteasy.reactive.RestQuery;
import java.util.Optional;

import static com.redhat.cloud.notifications.Constants.API_INTERNAL;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
Expand All @@ -30,10 +34,14 @@
public class ValidationResource {

private static final String EVENT_TYPE_NOT_FOUND_MSG = "No event type found for [bundle=%s, application=%s, eventType=%s]";
private static final String CERTIFICATE_NOT_AUTHORIZED_MSG = "This certificate is not authorized for [bundle=%s, application=%s]";

@Inject
ApplicationRepository applicationRepository;

@Inject
X509CertificateRepository x509CertificateRepository;

ConsoleCloudEventParser consoleCloudEventParser = new ConsoleCloudEventParser();

@GET
Expand Down Expand Up @@ -112,4 +120,21 @@ public Response validateConsoleCloudEvent(String action) {

return Response.ok().build();
}

@GET
@Path("/certificate")
@Produces(APPLICATION_JSON)
@APIResponses({
@APIResponse(responseCode = "200", description = "This certificate is valid for this bundle and application"),
@APIResponse(responseCode = "403", description = "This certificate is not valid for this bundle and application")
})
public X509Certificate validateCertificateAccordingBundleAndApp(@RestQuery String bundle, @RestQuery String application, @RestQuery String certificateSubjectDn) {
Optional<X509Certificate> gatewayCertificate = x509CertificateRepository.findCertificate(bundle, application, certificateSubjectDn);
if (gatewayCertificate.isEmpty()) {
String message = String.format(CERTIFICATE_NOT_AUTHORIZED_MSG, bundle, application);
throw new ForbiddenException(message);
} else {
return gatewayCertificate.get();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.redhat.cloud.notifications.routers.internal;

import com.redhat.cloud.notifications.db.repositories.X509CertificateRepository;
import com.redhat.cloud.notifications.models.X509Certificate;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import org.jboss.resteasy.reactive.RestPath;
import java.util.UUID;

import static com.redhat.cloud.notifications.Constants.API_INTERNAL;
import static com.redhat.cloud.notifications.auth.ConsoleIdentityProvider.RBAC_INTERNAL_ADMIN;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN;

@Path(API_INTERNAL + "/x509Certificates")
@RolesAllowed(RBAC_INTERNAL_ADMIN)
public class X509CertificateResource {

@Inject
X509CertificateRepository x509CertificateRepository;

@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public X509Certificate createCertificate(@NotNull @Valid X509Certificate certificate) {
return x509CertificateRepository.createCertificate(certificate);
}

@PUT
@Path("/{certificateId}")
@Consumes(APPLICATION_JSON)
@Produces(TEXT_PLAIN)
public Response updateCertificate(@RestPath UUID certificateId, @NotNull X509Certificate certificate) {
boolean updated = x509CertificateRepository.updateCertificate(certificateId, certificate);
if (updated) {
return Response.ok().build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}

@DELETE
@Path("/{certificateId}")
public boolean deleteCertificate(@RestPath UUID certificateId) {
return x509CertificateRepository.deleteCertificate(certificateId);
}
}
Loading

0 comments on commit a4a1f4c

Please sign in to comment.