Skip to content

Commit

Permalink
feat(uat): implement GGMQ scenario based on GGAD-1-T26 (#387)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgklika authored Aug 3, 2023
1 parent 66d062e commit 6f5bee9
Show file tree
Hide file tree
Showing 6 changed files with 422 additions and 86 deletions.
2 changes: 1 addition & 1 deletion uat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Example:
java -Dggc.archive=greengrass-nucleus-latest.zip -Dtest.log.path=logs -Dtags="@GGMQ and not @SkipOnWindows" -jar testing-features/target/client-devices-auth-testing-features.jar
```

@OffTheNetwork - scenarios with port 8883 blocked for some time
@OffTheNetwork - scenarios with port 8883 blocked for some time. These tests require OS level firewall should be activated on a machine there Nucleus is running: iptables on Linux and advfirewall on Windows.

To run tests matching ALL following criteria and all tags should be listed using "and" between tags
```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ enum Type {
/** MQTT message is received. */
EVENT_TYPE_MQTT_MESSAGE,

/** MQTT connection is disconnected. */
EVENT_TYPE_MQTT_DISCONNECTED,

// TODO: implement other events
// /** MQTT Connecttion established. */
// EVENT_TYPE_MQTT_CONNECTED,

// /** MQTT connection is disconnected. */
// EVENT_TYPE_MQTT_DISCONNECTED,

// /** Agent is registered and discovered. */
// EVENT_TYPE_AGENT_DISCOVERED,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.aws.greengrass.testing.mqtt.client.control.implementation.addon;

import com.aws.greengrass.testing.mqtt.client.Mqtt5Disconnect;
import com.aws.greengrass.testing.mqtt.client.control.api.ConnectionControl;
import com.aws.greengrass.testing.mqtt.client.control.api.addon.EventFilter;
import lombok.Getter;
import lombok.NonNull;

/**
* Implements event about MQTT connection has been disconnected.
*/
public class MqttDisconnectEvent extends EventImpl {

private final ConnectionControl connectionControl;

@Getter
private final Mqtt5Disconnect disconnect;

@Getter
private final String osError;

/**
* Creates instance of MqttDisconnectEvent.
*
* @param connectionControl the connection control which receives that message
* @param disconnect the disconnect gRPC event
* @param osError the OS error as received from the client
*/
public MqttDisconnectEvent(@NonNull ConnectionControl connectionControl, @NonNull Mqtt5Disconnect disconnect,
String osError) {
super(Type.EVENT_TYPE_MQTT_DISCONNECTED);
this.connectionControl = connectionControl;
this.disconnect = disconnect;
this.osError = osError;
}

@Override
public String getConnectionName() {
return connectionControl.getConnectionName();
}

@Override
public boolean isMatched(@NonNull EventFilter filter) {
// check type and timestamp
boolean matched = super.isMatched(filter);
if (!matched) {
return false;
}

// check connection
matched = compareConnection(filter.getConnectionControl(), filter.getAgentId(), filter.getConnectionId(),
filter.getConnectionName());
return matched;
}

@SuppressWarnings("PMD.CompareObjectsWithEquals")
private boolean compareConnection(ConnectionControl expectedConnectionControl, String agentId, Integer connectionId,
String connectionName) {
// 1'st priority
if (expectedConnectionControl != null) {
// compare references !
return expectedConnectionControl == connectionControl;
}

// 2'nd priority
if (agentId != null && connectionId != null) {
return agentId.equals(connectionControl.getAgentControl().getAgentId())
&& connectionId == connectionControl.getConnectionId();
}

// 3'th priority
// check connection name
return connectionName == null || connectionName.equals(getConnectionName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
import java.net.URI;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -43,10 +45,18 @@
@ScenarioScoped
public class BrokerCertificateSteps {
private static final int GET_CERTIFICATE_TIMEOUT_SEC = 5;
private static final int GET_PRINCIPALS_TIMEOUT_SEC = 5;

private final MqttBrokers mqttBrokers;

private final Map<String, X509Certificate> certificates = new HashMap<>();
private final Map<String, CertificateInfo> certificateInfos = new HashMap<>();

@AllArgsConstructor
@Getter
private static class CertificateInfo {
X509Certificate certificate; // TODO: can be list/array
Principal[] princinals;
}

@AllArgsConstructor
@Getter
Expand Down Expand Up @@ -75,18 +85,53 @@ public BrokerCertificateSteps(MqttBrokers mqttBrokers) {
*/
@Then("I verify the certificate {string} equals the certificate {string}")
public void verifyCertsAreEqual(String certNameA, String certNameB) {
X509Certificate certA = certificates.get(certNameA);
if (certA == null) {
throw new IllegalStateException(String.format("Certificate %s not found.", certNameA));
CertificateInfo certInfoA = getCertificateInfo(certNameA);
CertificateInfo certInfoB = getCertificateInfo(certNameB);
if (!certInfoA.getCertificate().equals(certInfoB.getCertificate())) {
throw new IllegalStateException("Certificates are differ");
}
}

X509Certificate certB = certificates.get(certNameB);
if (certB == null) {
throw new IllegalStateException(String.format("Certificate %s not found.", certNameB));
/**
* Checks is certificate contains endpoint in ubject alternative names extension.
*
* @param certName the name of certificate
* @param endpoint the name of endpoint or IP address
* @throws CertificateParsingException when could not extract subject alternative names from certificate
* @throws IllegalStateException on errors
*/
@Then("I verify that the subject alternative names of certificate {string} contains endpoint {string}")
public void verifyBrokerCertificateContainsEndpoint(String certName, String endpoint)
throws CertificateParsingException {
CertificateInfo certInfo = getCertificateInfo(certName);
Collection<List<?>> altNames = certInfo.getCertificate().getSubjectAlternativeNames();
if (altNames == null) {
throw new IllegalStateException("Missing Subject alternatiuve names of certificate");
}

if (!certA.equals(certB)) {
throw new IllegalStateException("Certificates are differ");
for (List<?> alt : altNames) {
Object altName = alt.get(1);
log.info("Cert alt name {}", altName);
if (endpoint.equals(altName)) {
return;
}
}

throw new IllegalStateException("Endpoint not found in the subject alternative names of certificate");
}

/**
* Checks is certificate's accepted issuer list is missing or empty.
*
* @param certName the name of certificate
* @throws IllegalStateException on errors
*/
@Then("I verify the TLS accepted issuer list of certificate {string} is empty")
public void verifyAcceptedIssuerListEmpty(String certName) {
CertificateInfo certInfo = getCertificateInfo(certName);
Principal[] clientCertIssuers = certInfo.getPrincinals();
if (clientCertIssuers != null && clientCertIssuers.length > 0) {
throw new IllegalStateException("Accepted issuer list is not empty");
}
}

Expand Down Expand Up @@ -129,9 +174,13 @@ public void retrieveServerCertificate(String brokerId, String certName)
throw new IllegalStateException("Certificate array is empty");
}

Principal[] principal = futures.getPrincinal().get(GET_PRINCIPALS_TIMEOUT_SEC,
TimeUnit.SECONDS);

// FIXME: strictly speaking we can have a array of certificates here
X509Certificate cert = serverCerts[0];
certificates.put(certName, cert);
log.info("Saved broker's '{}' certificate '{}'", brokerId, cert);
certificateInfos.put(certName, new CertificateInfo(cert, principal));
log.info("Saved broker's '{}' certificate info '{}'", brokerId, cert);
return;
} catch (IllegalStateException | ExecutionException | TimeoutException ex) {
lastException = ex;
Expand Down Expand Up @@ -232,4 +281,20 @@ public X509Certificate[] getAcceptedIssuers() {
return new CertificateFutures(serverCertsFut, clientCertIssuersFut);
}
}

/**
* Get information of certificate by name.
*
* @param certName the name of certificate
* @return certificate information
* @throws IllegalStateException on errors
*/
private CertificateInfo getCertificateInfo(String certName) {
CertificateInfo certInfo = certificateInfos.get(certName);
if (certInfo == null) {
throw new IllegalStateException(String.format("Certificate %s not found.", certName));
}

return certInfo;
}
}
Loading

0 comments on commit 6f5bee9

Please sign in to comment.