Skip to content

Commit

Permalink
RHCLOUD-24930
Browse files Browse the repository at this point in the history
  • Loading branch information
gwenneg committed Feb 3, 2024
1 parent 2909da0 commit 8824923
Show file tree
Hide file tree
Showing 36 changed files with 524 additions and 130 deletions.
11 changes: 11 additions & 0 deletions .rhcicd/clowdapp-connector-servicenow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ objects:
envName: ${ENV_NAME}
testing:
iqePlugin: eventing
dependencies:
- sources-api
optionalDependencies:
- drift
- notifications-backend
Expand Down Expand Up @@ -79,6 +81,13 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_DELAY}
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: ${NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_SOURCES_API_PSK
valueFrom:
secretKeyRef:
name: sources-api-psk
key: psk
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
value: ${NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS}
- name: NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE
Expand Down Expand Up @@ -164,6 +173,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
description: Maximum number of redelivery attempts (initial call not included)
value: "2"
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: "false"
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
description: Number of concurrent threads processing exchanges with SEDA
value: "20"
Expand Down
11 changes: 11 additions & 0 deletions .rhcicd/clowdapp-connector-splunk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ objects:
envName: ${ENV_NAME}
testing:
iqePlugin: eventing
dependencies:
- sources-api
optionalDependencies:
- drift
- notifications-backend
Expand Down Expand Up @@ -79,6 +81,13 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_DELAY}
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: ${NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_SOURCES_API_PSK
valueFrom:
secretKeyRef:
name: sources-api-psk
key: psk
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
value: ${NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS}
- name: NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE
Expand Down Expand Up @@ -164,6 +173,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
description: Maximum number of redelivery attempts (initial call not included)
value: "2"
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: "false"
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
description: Number of concurrent threads processing exchanges with SEDA
value: "20"
Expand Down
11 changes: 11 additions & 0 deletions .rhcicd/clowdapp-connector-webhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ objects:
envName: ${ENV_NAME}
testing:
iqePlugin: notifications
dependencies:
- sources-api
optionalDependencies:
- notifications-backend
kafkaTopics:
Expand Down Expand Up @@ -81,6 +83,13 @@ objects:
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_DELAY}
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
value: ${NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: ${NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED}
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_SOURCES_API_PSK
valueFrom:
secretKeyRef:
name: sources-api-psk
key: psk
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
value: ${NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS}
- name: NOTIFICATIONS_CONNECTOR_SEDA_QUEUE_SIZE
Expand Down Expand Up @@ -172,6 +181,8 @@ parameters:
- name: NOTIFICATIONS_CONNECTOR_REDELIVERY_MAX_ATTEMPTS
description: Maximum number of redelivery attempts (initial call not included)
value: "2"
- name: NOTIFICATIONS_CONNECTOR_SECRETS_LOADER_ENABLED
value: "false"
- name: NOTIFICATIONS_CONNECTOR_SEDA_CONCURRENT_CONSUMERS
description: Number of concurrent threads processing exchanges with SEDA
value: "20"
Expand Down
83 changes: 83 additions & 0 deletions connector-common-secrets-loader/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>

<artifactId>notifications-connector-common-secrets-loader</artifactId>

<parent>
<groupId>com.redhat.cloud.notifications</groupId>
<artifactId>notifications-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<dependencies>

<!-- Scope: compile -->

<!-- notifications modules -->
<dependency>
<groupId>com.redhat.cloud.notifications</groupId>
<artifactId>notifications-connector-common</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Quarkus -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>

</dependencies>

<build>
<plugins>

<!-- The following plugin is required to inject beans from this module into other modules. -->
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>

<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>

</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.redhat.cloud.notifications.connector.secrets;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Secret {

public String username;
public String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.redhat.cloud.notifications.connector.secrets;

public class SecretsExchangeProperty {

public static final String SECRET_ID = "secretId";
public static final String SECRET_PASSWORD = "secretPassword";
public static final String SECRET_USERNAME = "secretUsername";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.redhat.cloud.notifications.connector.secrets;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import static com.redhat.cloud.notifications.connector.ExchangeProperty.ORG_ID;
import static com.redhat.cloud.notifications.connector.secrets.SecretsExchangeProperty.SECRET_ID;
import static com.redhat.cloud.notifications.connector.secrets.SecretsExchangeProperty.SECRET_PASSWORD;
import static com.redhat.cloud.notifications.connector.secrets.SecretsExchangeProperty.SECRET_USERNAME;

@ApplicationScoped
public class SecretsLoader implements Processor {

private static final String SECRETS_LOADER_ENABLED = "notifications.connector.secrets-loader.enabled";
private static final String SOURCES_API_PSK = "notifications.connector.secrets-loader.sources-api-psk";
private static final String SOURCES_TIMER = "sources.get.secret.request";

@ConfigProperty(name = SECRETS_LOADER_ENABLED, defaultValue = "false")
boolean secretsLoaderEnabled;

@ConfigProperty(name = SOURCES_API_PSK)
String sourcesApiPsk;

@Inject
MeterRegistry meterRegistry;

@Inject
@RestClient
SourcesClient sourcesClient;

@Override
public void process(Exchange exchange) {
if (secretsLoaderEnabled) {
Long secretId = exchange.getProperty(SECRET_ID, Long.class);
if (secretId != null) {
String orgId = exchange.getProperty(ORG_ID, String.class);

// TODO Lower the log level after the testing phase.
Log.infof("Calling Sources to retrieve a secret [orgId=%s, secretId=%d]", orgId, secretId);

Timer.Sample timer = Timer.start(meterRegistry);
Secret secret = sourcesClient.getById(orgId, sourcesApiPsk, secretId);
timer.stop(meterRegistry.timer(SOURCES_TIMER));

if (secret.username != null && !secret.username.isBlank()) {
// TODO Lower the log level after the testing phase.
Log.info("Found a secret username in the response from Sources");
exchange.setProperty(SECRET_USERNAME, secret.username);
}

if (secret.password != null && !secret.password.isBlank()) {
// TODO Lower the log level after the testing phase.
Log.info("Found a secret password in the response from Sources");
exchange.setProperty(SECRET_PASSWORD, secret.password);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.redhat.cloud.notifications.connector.secrets;

import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.RestPath;

/**
* <p>REST Client for the Sources API. The OpenAPI spec is available at:</p>
*
* <ul>
* <li><a href="https://console.stage.redhat.com/docs/api/sources/v3.1">OpenApi v3.1 in the stage environment.</a></li>
* <li><a href="https://console.redhat.com/docs/api/sources/v3.1">OpenApi v3.1 in the production environment.</a></li>
* <li><a href="https://github.com/RedHatInsights/sources-api-go/blob/main/public/openapi-3-v3.1.json">OpenApi v3.1 JSON file on GitHub.</a></li>
* </ul>
*
* <p>Please be aware of the following:</p>
*
* <ul>
* <li>If sources is using a database backend, only the {@link Secret#password} field will get encrypted.</li>
* <li>On the other hand, if sources is using the AWS Secrets Manager, then the whole secret will get encrypted.</li>
* </ul>
*
* <p>The authentication to Sources works by using a service-to-service PSK that will be sent in the header that
* Sources expects. At the same time, the organization id will be sent so that Sources knows to which tenants belongs
* the operation that is going to be performed.</p>
*/
@RegisterRestClient(configKey = "sources")
public interface SourcesClient {

/**
* Get a single secret from Sources. In this case we need to hit the internal endpoint —which is only available for
* requests coming from inside the cluster— to be able to get the password of these secrets.
* @param xRhSourcesOrgId the organization id related to this operation for the tenant identification.
* @param xRhSourcesPsk the sources PSK required for the authorization.
* @param id the secret id.
* @return a {@link Secret} instance.
*/
@GET
@Path("/internal/v2.0/secrets/{id}")
@Retry
Secret getById(
@HeaderParam("x-rh-sources-org-id") @NotBlank String xRhSourcesOrgId,
@HeaderParam("x-rh-sources-psk") @NotBlank String xRhSourcesPsk,
@RestPath long id
);

/**
* Throws a runtime exception with the client's response for an easier debugging.
* @param response the received response from Sources.
* @return the {@link RuntimeException} to be thrown.
*/
@ClientExceptionMapper
static RuntimeException toException(final Response response) {
final var errMessage = String.format("Sources responded with a %s status: %s", response.getStatus(), response.readEntity(String.class));

throw new WebApplicationException(errMessage, response);
}
}
7 changes: 6 additions & 1 deletion connector-servicenow/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,17 @@

<!-- Scope: compile -->

<!-- Notifications module -->
<!-- Notifications modules -->
<dependency>
<groupId>com.redhat.cloud.notifications</groupId>
<artifactId>notifications-connector-common-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.redhat.cloud.notifications</groupId>
<artifactId>notifications-connector-common-secrets-loader</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Target URL validation -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.BasicHttpContext;

import static com.redhat.cloud.notifications.connector.servicenow.ExchangeProperty.AUTHENTICATION_TOKEN;
import static com.redhat.cloud.notifications.connector.secrets.SecretsExchangeProperty.SECRET_PASSWORD;

public class BasicAuthenticationProcessor implements Processor {

private static final String USERNAME = "rh_insights_integration";

public void process(Exchange exchange) throws AuthenticationException {

String token = exchange.getProperty(AUTHENTICATION_TOKEN, String.class);
String token = exchange.getProperty(SECRET_PASSWORD, String.class);
if (token == null || token.isBlank()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
public class ExchangeProperty {

public static final String ACCOUNT_ID = "accountId";
public static final String AUTHENTICATION_TOKEN = "authenticationToken";
public static final String TARGET_URL_NO_SCHEME = "targetUrlNoScheme";
public static final String TRUST_ALL = "trustAll";
}
Loading

0 comments on commit 8824923

Please sign in to comment.