From 95dab8d7596e50e909e27b11d2002f66e4f5200e Mon Sep 17 00:00:00 2001 From: Petr Hadraba Date: Fri, 24 Sep 2021 14:45:57 +0200 Subject: [PATCH] Issue #207: implemented Agent-local operations Issue #224: implemented Consul Intentions --- .../CertificateChainTypeAdapterFactory.java | 56 +++++ .../consul/json/CertificateTypeAdapter.java | 64 +++++ .../json/OffsetDateTimeTypeAdapter.java | 32 +++ .../transport/AbstractHttpTransport.java | 32 ++- .../ecwid/consul/transport/HttpTransport.java | 4 +- .../com/ecwid/consul/v1/ConsulClient.java | 81 ++++++- .../com/ecwid/consul/v1/ConsulRawClient.java | 14 ++ .../ecwid/consul/v1/agent/AgentClient.java | 23 +- .../consul/v1/agent/AgentConsulClient.java | 59 ++++- .../v1/agent/model/AuthorizeRequest.java | 83 +++++++ .../v1/agent/model/AuthorizeResponse.java | 56 +++++ .../ecwid/consul/v1/agent/model/CaRoot.java | 227 ++++++++++++++++++ .../ecwid/consul/v1/agent/model/CaRoots.java | 58 +++++ .../v1/agent/model/LeafCertificate.java | 161 +++++++++++++ .../consul/v1/connect/ConnectClient.java | 16 ++ .../v1/connect/ConnectConsulClient.java | 80 ++++++ .../intentions/IntentionDeleteRequest.java | 54 +++++ .../intentions/IntentionListRequest.java | 66 +++++ .../intentions/IntentionUpsertRequest.java | 79 ++++++ .../connect/intentions/IntentionsClient.java | 17 ++ .../intentions/IntentionsConsulClient.java | 122 ++++++++++ .../connect/intentions/model/Intention.java | 85 +++++++ .../model/IntentionHttpHeaderPermission.java | 122 ++++++++++ .../model/IntentionHttpPermission.java | 98 ++++++++ .../intentions/model/IntentionPermission.java | 57 +++++ .../intentions/model/IntentionResponse.java | 140 +++++++++++ .../connect/model/CaConfigurationRequest.java | 70 ++++++ .../model/CaConfigurationResponse.java | 83 +++++++ .../consul/v1/connect/model/CaRoots.java | 46 ++++ src/test/java/com/ecwid/consul/TestUtils.java | 58 +++++ .../ecwid/consul/json/IntermediatesTest.java | 45 ++++ .../v1/agent/AuthorizeConsulClientTest.java | 62 +++++ .../consul/v1/agent/AuthorizeDtoTest.java | 26 ++ .../v1/agent/CaRootsConsulClientTest.java | 57 +++++ .../ecwid/consul/v1/agent/CaRootsDtoTest.java | 39 +++ .../LeafCertificateConsulClientTest.java | 59 +++++ .../v1/agent/LeafCertificateDtoTest.java | 20 ++ .../v1/connect/ConnectConsulClientTest.java | 77 ++++++ .../intentions/EqualsVerifierTest.java | 23 ++ .../intentions/model/EqualsVerifierTest.java | 33 +++ .../connect/model/CaConfigurationDtoTest.java | 24 ++ .../v1/connect/model/CaRootsDtoTest.java | 27 +++ .../com/ecwid/consul/isrg-root-x1.pem | 31 +++ .../com/ecwid/consul/isrg-root-x2.pem | 14 ++ .../consul/json/empty-intermediates.json | 22 ++ .../ecwid/consul/json/null-intermediates.json | 22 ++ .../ecwid/consul/json/two-intermediates.json | 25 ++ 47 files changed, 2730 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/ecwid/consul/json/CertificateChainTypeAdapterFactory.java create mode 100644 src/main/java/com/ecwid/consul/json/CertificateTypeAdapter.java create mode 100644 src/main/java/com/ecwid/consul/json/OffsetDateTimeTypeAdapter.java create mode 100644 src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeRequest.java create mode 100644 src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeResponse.java create mode 100644 src/main/java/com/ecwid/consul/v1/agent/model/CaRoot.java create mode 100644 src/main/java/com/ecwid/consul/v1/agent/model/CaRoots.java create mode 100644 src/main/java/com/ecwid/consul/v1/agent/model/LeafCertificate.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/ConnectClient.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/ConnectConsulClient.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionDeleteRequest.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionListRequest.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionUpsertRequest.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsClient.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsConsulClient.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/model/Intention.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpHeaderPermission.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpPermission.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionPermission.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionResponse.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationRequest.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationResponse.java create mode 100644 src/main/java/com/ecwid/consul/v1/connect/model/CaRoots.java create mode 100644 src/test/java/com/ecwid/consul/TestUtils.java create mode 100644 src/test/java/com/ecwid/consul/json/IntermediatesTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/AuthorizeConsulClientTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/AuthorizeDtoTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/CaRootsConsulClientTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/CaRootsDtoTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/LeafCertificateConsulClientTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/agent/LeafCertificateDtoTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/connect/ConnectConsulClientTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/connect/intentions/EqualsVerifierTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/connect/intentions/model/EqualsVerifierTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/connect/model/CaConfigurationDtoTest.java create mode 100644 src/test/java/com/ecwid/consul/v1/connect/model/CaRootsDtoTest.java create mode 100644 src/test/resources/com/ecwid/consul/isrg-root-x1.pem create mode 100644 src/test/resources/com/ecwid/consul/isrg-root-x2.pem create mode 100644 src/test/resources/com/ecwid/consul/json/empty-intermediates.json create mode 100644 src/test/resources/com/ecwid/consul/json/null-intermediates.json create mode 100644 src/test/resources/com/ecwid/consul/json/two-intermediates.json diff --git a/src/main/java/com/ecwid/consul/json/CertificateChainTypeAdapterFactory.java b/src/main/java/com/ecwid/consul/json/CertificateChainTypeAdapterFactory.java new file mode 100644 index 0000000..94e1ce3 --- /dev/null +++ b/src/main/java/com/ecwid/consul/json/CertificateChainTypeAdapterFactory.java @@ -0,0 +1,56 @@ +package com.ecwid.consul.json; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CertificateChainTypeAdapterFactory implements TypeAdapterFactory { + + private final CertificateTypeAdapter certificateTypeAdapter = new CertificateTypeAdapter(); + + @Override + @SuppressWarnings("unchecked") + public TypeAdapter create(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + if (!Collection.class.isAssignableFrom(rawType)) { + return null; + } + return new TypeAdapter() { + + @Override + public void write(JsonWriter out, T value) throws IOException { + List certificates = (List) value; + if (certificates == null) { + out.nullValue(); + } else { + out.beginArray(); + for (Certificate certificate : certificates) { + certificateTypeAdapter.write(out, certificate); + } + out.endArray(); + } + } + + @Override + public T read(JsonReader in) throws IOException { + final List certificates = new ArrayList<>(); + in.beginArray(); + while (in.peek() == JsonToken.STRING) { + certificates.add(certificateTypeAdapter.read(in)); + } + in.endArray(); + return (T) certificates; + } + }; + } + +} diff --git a/src/main/java/com/ecwid/consul/json/CertificateTypeAdapter.java b/src/main/java/com/ecwid/consul/json/CertificateTypeAdapter.java new file mode 100644 index 0000000..dd426f1 --- /dev/null +++ b/src/main/java/com/ecwid/consul/json/CertificateTypeAdapter.java @@ -0,0 +1,64 @@ +package com.ecwid.consul.json; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Base64; + +public class CertificateTypeAdapter extends TypeAdapter { + + private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; + + private static final String END_CERT = "-----END CERTIFICATE-----"; + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private static final int LINE_LENGTH = 64; + + @Override + public void write(JsonWriter out, Certificate value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + try { + Base64.Encoder encoder = Base64.getMimeEncoder( + LINE_LENGTH, LINE_SEPARATOR.getBytes(StandardCharsets.UTF_8)); + final String encoded = BEGIN_CERT + + LINE_SEPARATOR + + new String(encoder.encode(value.getEncoded()), StandardCharsets.UTF_8) + + LINE_SEPARATOR + + END_CERT; + out.value(encoded); + } catch (CertificateEncodingException ex) { + throw new IOException(ex); + } + } + } + + @Override + public Certificate read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + String certificate = in.nextString(); + try { + return CertificateFactory.getInstance("X509") + .generateCertificate( + new ByteArrayInputStream( + certificate.getBytes(StandardCharsets.UTF_8))); + } catch (CertificateException ex) { + throw new IOException(ex); + } + } + } + +} diff --git a/src/main/java/com/ecwid/consul/json/OffsetDateTimeTypeAdapter.java b/src/main/java/com/ecwid/consul/json/OffsetDateTimeTypeAdapter.java new file mode 100644 index 0000000..47ab186 --- /dev/null +++ b/src/main/java/com/ecwid/consul/json/OffsetDateTimeTypeAdapter.java @@ -0,0 +1,32 @@ +package com.ecwid.consul.json; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.time.OffsetDateTime; + +public class OffsetDateTimeTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, OffsetDateTime value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.toString()); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + String timestamp = in.nextString(); + return OffsetDateTime.parse(timestamp); + } + } + +} diff --git a/src/main/java/com/ecwid/consul/transport/AbstractHttpTransport.java b/src/main/java/com/ecwid/consul/transport/AbstractHttpTransport.java index 4e479fd..f8a7e06 100644 --- a/src/main/java/com/ecwid/consul/transport/AbstractHttpTransport.java +++ b/src/main/java/com/ecwid/consul/transport/AbstractHttpTransport.java @@ -1,21 +1,22 @@ package com.ecwid.consul.transport; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.logging.Logger; import org.apache.http.Header; import org.apache.http.HeaderIterator; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; -import java.util.stream.StreamSupport; - public abstract class AbstractHttpTransport implements HttpTransport { private static final Logger log = Logger.getLogger(AbstractHttpTransport.class.getName()); @@ -56,6 +57,19 @@ public HttpResponse makeDeleteRequest(HttpRequest request) { return executeRequest(httpDelete); } + @Override + public HttpResponse makePostRequest(HttpRequest request) { + HttpPost httpPost = new HttpPost(request.getUrl()); + addHeadersToRequest(httpPost, request.getHeaders()); + if (request.getContent() != null) { + httpPost.setEntity(new StringEntity(request.getContent(), StandardCharsets.UTF_8)); + } else { + httpPost.setEntity(new ByteArrayEntity(request.getBinaryContent())); + } + + return executeRequest(httpPost); + } + /** * You should override this method to instantiate ready to use HttpClient * diff --git a/src/main/java/com/ecwid/consul/transport/HttpTransport.java b/src/main/java/com/ecwid/consul/transport/HttpTransport.java index 60e9cfe..f83346c 100644 --- a/src/main/java/com/ecwid/consul/transport/HttpTransport.java +++ b/src/main/java/com/ecwid/consul/transport/HttpTransport.java @@ -1,7 +1,5 @@ package com.ecwid.consul.transport; -import java.util.Map; - /** * @author Vasily Vasilkov (vgv@ecwid.com) */ @@ -13,4 +11,6 @@ public interface HttpTransport { public HttpResponse makeDeleteRequest(HttpRequest request); + public HttpResponse makePostRequest(HttpRequest request); + } diff --git a/src/main/java/com/ecwid/consul/v1/ConsulClient.java b/src/main/java/com/ecwid/consul/v1/ConsulClient.java index 8d83964..246f927 100644 --- a/src/main/java/com/ecwid/consul/v1/ConsulClient.java +++ b/src/main/java/com/ecwid/consul/v1/ConsulClient.java @@ -11,6 +11,16 @@ import com.ecwid.consul.v1.agent.model.*; import com.ecwid.consul.v1.catalog.*; import com.ecwid.consul.v1.catalog.model.*; +import com.ecwid.consul.v1.connect.ConnectClient; +import com.ecwid.consul.v1.connect.ConnectConsulClient; +import com.ecwid.consul.v1.connect.intentions.IntentionDeleteRequest; +import com.ecwid.consul.v1.connect.intentions.IntentionListRequest; +import com.ecwid.consul.v1.connect.intentions.IntentionUpsertRequest; +import com.ecwid.consul.v1.connect.intentions.IntentionsClient; +import com.ecwid.consul.v1.connect.intentions.IntentionsConsulClient; +import com.ecwid.consul.v1.connect.intentions.model.IntentionResponse; +import com.ecwid.consul.v1.connect.model.CaConfigurationRequest; +import com.ecwid.consul.v1.connect.model.CaConfigurationResponse; import com.ecwid.consul.v1.coordinate.CoordinateClient; import com.ecwid.consul.v1.coordinate.CoordinateConsulClient; import com.ecwid.consul.v1.coordinate.model.Datacenter; @@ -56,6 +66,8 @@ public class ConsulClient implements AclClient, AgentClient, CatalogClient, + ConnectClient, + IntentionsClient, CoordinateClient, EventClient, HealthClient, @@ -67,6 +79,8 @@ public class ConsulClient implements private final AclClient aclClient; private final AgentClient agentClient; private final CatalogClient catalogClient; + private final ConnectClient connectClient; + private final IntentionsClient intentionsClient; private final CoordinateClient coordinateClient; private final EventClient eventClient; private final HealthClient healthClient; @@ -79,6 +93,8 @@ public ConsulClient(ConsulRawClient rawClient) { aclClient = new AclConsulClient(rawClient); agentClient = new AgentConsulClient(rawClient); catalogClient = new CatalogConsulClient(rawClient); + connectClient = new ConnectConsulClient(rawClient); + intentionsClient = new IntentionsConsulClient(rawClient); coordinateClient = new CoordinateConsulClient(rawClient); eventClient = new EventConsulClient(rawClient); healthClient = new HealthConsulClient(rawClient); @@ -209,7 +225,7 @@ public Response> getAgentMembers() { public Response getAgentSelf() { return agentClient.getAgentSelf(); } - + @Override public Response getAgentSelf(String token) { return agentClient.getAgentSelf(token); @@ -335,7 +351,27 @@ public Response agentReload() { return agentClient.agentReload(); } - // ------------------------------------------------------------------------------------------- + @Override + public Response agentAuthorize(AuthorizeRequest authorizeRequest) { + return agentClient.agentAuthorize(authorizeRequest); + } + + @Override + public Response agentCaRoots() { + return agentClient.agentCaRoots(); + } + + @Override + public Response agentLeafCertificate(String service, String namespace) { + return agentClient.agentLeafCertificate(service, namespace); + } + + @Override + public Response agentLeafCertificate(String service) { + return agentClient.agentLeafCertificate(service); + } + +// ------------------------------------------------------------------------------------------- // Catalog @Override @@ -455,6 +491,47 @@ public Response getCatalogNode(String nodeName, QueryParams queryPa return catalogClient.getCatalogNode(nodeName, queryParams); } + // ------------------------------------------------------------------------------------------- + // Connect + + @Override + public Response connectListCaRoots() { + return connectClient.connectListCaRoots(); + } + + @Override + public Response connectGetCaConfiguration() { + return connectClient.connectGetCaConfiguration(); + } + + @Override + public Response connectUpdateCaConfiguration(CaConfigurationRequest request) { + return connectClient.connectUpdateCaConfiguration(request); + } + +// ------------------------------------------------------------------------------------------- + // Intentions + + @Override + public Response createIntention(IntentionUpsertRequest request, String token) { + return intentionsClient.createIntention(request, token); + } + + @Override + public Response updateIntention(IntentionUpsertRequest request, String token) { + return intentionsClient.updateIntention(request, token); + } + + @Override + public Response> listIntentions(IntentionListRequest request, String token) { + return intentionsClient.listIntentions(request, token); + } + + @Override + public Response deleteIntention(IntentionDeleteRequest request, String token) { + return intentionsClient.deleteIntention(request, token); + } + // ------------------------------------------------------------------------------------------- // Coordinates diff --git a/src/main/java/com/ecwid/consul/v1/ConsulRawClient.java b/src/main/java/com/ecwid/consul/v1/ConsulRawClient.java index f5117f3..40b9298 100644 --- a/src/main/java/com/ecwid/consul/v1/ConsulRawClient.java +++ b/src/main/java/com/ecwid/consul/v1/ConsulRawClient.java @@ -189,6 +189,20 @@ public HttpResponse makeDeleteRequest(Request request) { return httpTransport.makeDeleteRequest(httpRequest); } + public HttpResponse makePostRequest(Request request) { + String url = prepareUrl(agentAddress + request.getEndpoint()); + url = Utils.generateUrl(url, request.getUrlParameters()); + + HttpRequest httpRequest = HttpRequest.Builder.newBuilder() + .setUrl(url) + .addHeaders(Utils.createTokenMap(request.getToken())) + .setContent(request.getContent()) + .setBinaryContent(request.getBinaryContent()) + .build(); + + return httpTransport.makePostRequest(httpRequest); + } + private String prepareUrl(String url) { if (url.contains(" ")) { // temp hack for old clients who did manual encoding and just use %20 diff --git a/src/main/java/com/ecwid/consul/v1/agent/AgentClient.java b/src/main/java/com/ecwid/consul/v1/agent/AgentClient.java index fc9f186..a41516d 100644 --- a/src/main/java/com/ecwid/consul/v1/agent/AgentClient.java +++ b/src/main/java/com/ecwid/consul/v1/agent/AgentClient.java @@ -1,8 +1,16 @@ package com.ecwid.consul.v1.agent; import com.ecwid.consul.v1.Response; -import com.ecwid.consul.v1.agent.model.*; - +import com.ecwid.consul.v1.agent.model.AuthorizeRequest; +import com.ecwid.consul.v1.agent.model.AuthorizeResponse; +import com.ecwid.consul.v1.agent.model.CaRoots; +import com.ecwid.consul.v1.agent.model.Check; +import com.ecwid.consul.v1.agent.model.LeafCertificate; +import com.ecwid.consul.v1.agent.model.Member; +import com.ecwid.consul.v1.agent.model.NewCheck; +import com.ecwid.consul.v1.agent.model.NewService; +import com.ecwid.consul.v1.agent.model.Self; +import com.ecwid.consul.v1.agent.model.Service; import java.util.List; import java.util.Map; @@ -18,7 +26,7 @@ public interface AgentClient { public Response> getAgentMembers(); public Response getAgentSelf(); - + public Response getAgentSelf(String token); public Response agentSetMaintenance(boolean maintenanceEnabled); @@ -68,4 +76,13 @@ public interface AgentClient { public Response agentServiceSetMaintenance(String serviceId, boolean maintenanceEnabled, String reason); public Response agentReload(); + + public Response agentAuthorize(AuthorizeRequest authorizeRequest); + + public Response agentCaRoots(); + + public Response agentLeafCertificate(String service, String namespace); + + public Response agentLeafCertificate(String service); + } diff --git a/src/main/java/com/ecwid/consul/v1/agent/AgentConsulClient.java b/src/main/java/com/ecwid/consul/v1/agent/AgentConsulClient.java index d4af7b2..7eb9e17 100644 --- a/src/main/java/com/ecwid/consul/v1/agent/AgentConsulClient.java +++ b/src/main/java/com/ecwid/consul/v1/agent/AgentConsulClient.java @@ -7,10 +7,19 @@ import com.ecwid.consul.transport.TLSConfig; import com.ecwid.consul.v1.ConsulRawClient; import com.ecwid.consul.v1.OperationException; +import com.ecwid.consul.v1.Request; import com.ecwid.consul.v1.Response; -import com.ecwid.consul.v1.agent.model.*; +import com.ecwid.consul.v1.agent.model.AuthorizeRequest; +import com.ecwid.consul.v1.agent.model.AuthorizeResponse; +import com.ecwid.consul.v1.agent.model.CaRoots; +import com.ecwid.consul.v1.agent.model.Check; +import com.ecwid.consul.v1.agent.model.LeafCertificate; +import com.ecwid.consul.v1.agent.model.Member; +import com.ecwid.consul.v1.agent.model.NewCheck; +import com.ecwid.consul.v1.agent.model.NewService; +import com.ecwid.consul.v1.agent.model.Self; +import com.ecwid.consul.v1.agent.model.Service; import com.google.gson.reflect.TypeToken; - import java.util.List; import java.util.Map; @@ -204,7 +213,7 @@ public Response agentCheckPass(String checkId, String note, String token) UrlParameters tokenParameter = token != null ? new SingleUrlParameters("token", token) : null; HttpResponse httpResponse = rawClient.makePutRequest("/v1/agent/check/pass/" + checkId, "", noteParameter, tokenParameter); - + if (httpResponse.getStatusCode() == 200) { return new Response(null, httpResponse); } else { @@ -327,4 +336,48 @@ public Response agentReload() { } } + + @Override + public Response agentAuthorize(AuthorizeRequest authorizeRequest) { + Request request = Request.Builder.newBuilder() + .setEndpoint("/v1/agent/connect/authorize") + .setContent(GsonFactory.getGson().toJson(authorizeRequest)) + .build(); + + HttpResponse httpResponse = rawClient.makePostRequest(request); + + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson().fromJson(httpResponse.getContent(), AuthorizeResponse.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response agentCaRoots() { + HttpResponse httpResponse = rawClient.makeGetRequest("/v1/agent/connect/ca/roots"); + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson().fromJson(httpResponse.getContent(), CaRoots.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response agentLeafCertificate(String service, String namespace) { + HttpResponse httpResponse = rawClient.makeGetRequest( + "/v1/agent/connect/ca/leaf/" + service, + new SingleUrlParameters("ns", namespace)); + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson().fromJson(httpResponse.getContent(), LeafCertificate.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response agentLeafCertificate(String service) { + return agentLeafCertificate(service, null); + } + } diff --git a/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeRequest.java b/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeRequest.java new file mode 100644 index 0000000..2168c8a --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeRequest.java @@ -0,0 +1,83 @@ +package com.ecwid.consul.v1.agent.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; +import java.util.StringJoiner; + +public class AuthorizeRequest { + + @SerializedName("Target") + private String target; + + @SerializedName("ClientCertURI") + private String clientCertUri; + + @SerializedName("ClientCertSerial") + private String clientCertSerial; + + @SerializedName("Namespace") + private String namespace = "default"; + + public String getTarget() { + return target; + } + + public void setTarget(final String target) { + this.target = target; + } + + public String getClientCertUri() { + return clientCertUri; + } + + public void setClientCertUri(final String clientCertUri) { + this.clientCertUri = clientCertUri; + } + + public String getClientCertSerial() { + return clientCertSerial; + } + + public void setClientCertSerial(final String clientCertSerial) { + this.clientCertSerial = clientCertSerial; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(final String namespace) { + this.namespace = namespace; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final AuthorizeRequest authorize = (AuthorizeRequest) o; + return Objects.equals(target, authorize.target) + && Objects.equals(clientCertUri, authorize.clientCertUri) + && Objects.equals(clientCertSerial, authorize.clientCertSerial) + && Objects.equals(namespace, authorize.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(target, clientCertUri, clientCertSerial, namespace); + } + + @Override + public String toString() { + return new StringJoiner(", ", AuthorizeRequest.class.getSimpleName() + "[", "]") + .add("target='" + target + "'") + .add("clientCertUri='" + clientCertUri + "'") + .add("clientCertSerial='" + clientCertSerial + "'") + .add("namespace='" + namespace + "'") + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeResponse.java b/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeResponse.java new file mode 100644 index 0000000..a27ae1d --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/agent/model/AuthorizeResponse.java @@ -0,0 +1,56 @@ +package com.ecwid.consul.v1.agent.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; +import java.util.StringJoiner; + +public class AuthorizeResponse { + + @SerializedName("Authorized") + private boolean authorized; + + @SerializedName("Reason") + public String reason; + + public boolean isAuthorized() { + return authorized; + } + + public void setAuthorized(final boolean authorized) { + this.authorized = authorized; + } + + public String getReason() { + return reason; + } + + public void setReason(final String reason) { + this.reason = reason; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final AuthorizeResponse that = (AuthorizeResponse) o; + return authorized == that.authorized && Objects.equals(reason, that.reason); + } + + @Override + public int hashCode() { + return Objects.hash(authorized, reason); + } + + @Override + public String toString() { + return new StringJoiner(", ", AuthorizeResponse.class.getSimpleName() + "[", "]") + .add("authorized=" + authorized) + .add("reason='" + reason + "'") + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/agent/model/CaRoot.java b/src/main/java/com/ecwid/consul/v1/agent/model/CaRoot.java new file mode 100644 index 0000000..69ae1a0 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/agent/model/CaRoot.java @@ -0,0 +1,227 @@ +package com.ecwid.consul.v1.agent.model; + +import com.ecwid.consul.json.CertificateChainTypeAdapterFactory; +import com.ecwid.consul.json.OffsetDateTimeTypeAdapter; +import com.ecwid.consul.json.CertificateTypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import java.security.cert.Certificate; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; + +public class CaRoot { + + @SerializedName("ID") + private String id; + + @SerializedName("Name") + private String name; + + @SerializedName("SerialNumber") + private int serialNumber; + + @SerializedName("SigningKeyID") + private String signingKeyId; + + @SerializedName("ExternalTrustDomain") + private String externalTrustDomain; + + @SerializedName("NotBefore") + @JsonAdapter(OffsetDateTimeTypeAdapter.class) + private OffsetDateTime notBefore; + + @SerializedName("NotAfter") + @JsonAdapter(OffsetDateTimeTypeAdapter.class) + private OffsetDateTime notAfter; + + @SerializedName("RootCert") + @JsonAdapter(CertificateTypeAdapter.class) + private Certificate rootCert; + + @SerializedName("IntermediateCerts") + @JsonAdapter(CertificateChainTypeAdapterFactory.class) + private List intermediateCerts; + + @SerializedName("Active") + private boolean active; + + @SerializedName("PrivateKeyType") + private String privateKeyType; + + @SerializedName("PrivateKeyBits") + private int privateKeyBits; + + @SerializedName("CreateIndex") + private int createIndex; + + @SerializedName("ModifyIndex") + private int modifyIndex; + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public int getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(final int serialNumber) { + this.serialNumber = serialNumber; + } + + public String getSigningKeyId() { + return signingKeyId; + } + + public void setSigningKeyId(final String signingKeyId) { + this.signingKeyId = signingKeyId; + } + + public String getExternalTrustDomain() { + return externalTrustDomain; + } + + public void setExternalTrustDomain(final String externalTrustDomain) { + this.externalTrustDomain = externalTrustDomain; + } + + public OffsetDateTime getNotBefore() { + return notBefore; + } + + public void setNotBefore(final OffsetDateTime notBefore) { + this.notBefore = notBefore; + } + + public OffsetDateTime getNotAfter() { + return notAfter; + } + + public void setNotAfter(final OffsetDateTime notAfter) { + this.notAfter = notAfter; + } + + public Certificate getRootCert() { + return rootCert; + } + + public void setRootCert(final Certificate rootCert) { + this.rootCert = rootCert; + } + + public List getIntermediateCerts() { + return intermediateCerts; + } + + public void setIntermediateCerts(final List intermediateCerts) { + this.intermediateCerts = intermediateCerts; + } + + public boolean isActive() { + return active; + } + + public void setActive(final boolean active) { + this.active = active; + } + + public String getPrivateKeyType() { + return privateKeyType; + } + + public void setPrivateKeyType(final String privateKeyType) { + this.privateKeyType = privateKeyType; + } + + public int getPrivateKeyBits() { + return privateKeyBits; + } + + public void setPrivateKeyBits(final int privateKeyBits) { + this.privateKeyBits = privateKeyBits; + } + + public int getCreateIndex() { + return createIndex; + } + + public void setCreateIndex(final int createIndex) { + this.createIndex = createIndex; + } + + public int getModifyIndex() { + return modifyIndex; + } + + public void setModifyIndex(final int modifyIndex) { + this.modifyIndex = modifyIndex; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CaRoot caRoot = (CaRoot) o; + return serialNumber == caRoot.serialNumber + && active == caRoot.active + && privateKeyBits == caRoot.privateKeyBits + && createIndex == caRoot.createIndex + && modifyIndex == caRoot.modifyIndex + && Objects.equals(id, caRoot.id) + && Objects.equals(name, caRoot.name) + && Objects.equals(signingKeyId, caRoot.signingKeyId) + && Objects.equals(externalTrustDomain, caRoot.externalTrustDomain) + && Objects.equals(notBefore, caRoot.notBefore) + && Objects.equals(notAfter, caRoot.notAfter) + && Objects.equals(rootCert, caRoot.rootCert) + && Objects.equals(intermediateCerts, caRoot.intermediateCerts) + && Objects.equals(privateKeyType, caRoot.privateKeyType); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, serialNumber, signingKeyId, + externalTrustDomain, notBefore, notAfter, rootCert, + intermediateCerts, active, privateKeyType, privateKeyBits, + createIndex, modifyIndex); + } + + @Override + public String toString() { + return new StringJoiner(", ", CaRoot.class.getSimpleName() + "[", "]") + .add("id='" + id + "'") + .add("name='" + name + "'") + .add("serialNumber=" + serialNumber) + .add("signingKeyId='" + signingKeyId + "'") + .add("externalTrustDomain='" + externalTrustDomain + "'") + .add("notBefore='" + notBefore + "'") + .add("notAfter='" + notAfter + "'") + .add("rootCert='" + rootCert + "'") + .add("intermediateCerts=" + intermediateCerts) + .add("active=" + active) + .add("privateKeyType='" + privateKeyType + "'") + .add("privateKeyBits=" + privateKeyBits) + .add("createIndex=" + createIndex) + .add("modifyIndex=" + modifyIndex) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/agent/model/CaRoots.java b/src/main/java/com/ecwid/consul/v1/agent/model/CaRoots.java new file mode 100644 index 0000000..b06f513 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/agent/model/CaRoots.java @@ -0,0 +1,58 @@ +package com.ecwid.consul.v1.agent.model; + +import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; + +public class CaRoots { + + @SerializedName("ActiveRootID") + private String activeRootId; + + @SerializedName("Roots") + private List roots = new ArrayList<>(); + + public String getActiveRootId() { + return activeRootId; + } + + public void setActiveRootId(final String activeRootId) { + this.activeRootId = activeRootId; + } + + public List getRoots() { + return roots; + } + + public void setRoots(final List roots) { + this.roots = roots; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CaRoots caRoots = (CaRoots) o; + return Objects.equals(activeRootId, caRoots.activeRootId) && Objects.equals(roots, caRoots.roots); + } + + @Override + public int hashCode() { + return Objects.hash(activeRootId, roots); + } + + @Override + public String toString() { + return new StringJoiner(", ", CaRoots.class.getSimpleName() + "[", "]") + .add("activeRootId='" + activeRootId + "'") + .add("roots=" + roots) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/agent/model/LeafCertificate.java b/src/main/java/com/ecwid/consul/v1/agent/model/LeafCertificate.java new file mode 100644 index 0000000..a5448c7 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/agent/model/LeafCertificate.java @@ -0,0 +1,161 @@ +package com.ecwid.consul.v1.agent.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; +import java.util.StringJoiner; + +public class LeafCertificate { + + @SerializedName("SerialNumber") + private String serialNumber; + + @SerializedName("CertPEM") + private String certPEM; + + @SerializedName("PrivateKeyPEM") + private String privateKeyPEM; + + @SerializedName("Service") + private String service; + + @SerializedName("ServiceURI") + private String serviceURI; + + @SerializedName("ValidAfter") + private String validAfter; + + @SerializedName("ValidBefore") + private String validBefore; + + @SerializedName("Namespace") + private String namespace; + + @SerializedName("CreateIndex") + private int createIndex; + + @SerializedName("ModifyIndex") + private int modifyIndex; + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(final String serialNumber) { + this.serialNumber = serialNumber; + } + + public String getCertPEM() { + return certPEM; + } + + public void setCertPEM(final String certPEM) { + this.certPEM = certPEM; + } + + public String getPrivateKeyPEM() { + return privateKeyPEM; + } + + public void setPrivateKeyPEM(final String privateKeyPEM) { + this.privateKeyPEM = privateKeyPEM; + } + + public String getService() { + return service; + } + + public void setService(final String service) { + this.service = service; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(final String serviceURI) { + this.serviceURI = serviceURI; + } + + public String getValidAfter() { + return validAfter; + } + + public void setValidAfter(final String validAfter) { + this.validAfter = validAfter; + } + + public String getValidBefore() { + return validBefore; + } + + public void setValidBefore(final String validBefore) { + this.validBefore = validBefore; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(final String namespace) { + this.namespace = namespace; + } + + public int getCreateIndex() { + return createIndex; + } + + public void setCreateIndex(final int createIndex) { + this.createIndex = createIndex; + } + + public int getModifyIndex() { + return modifyIndex; + } + + public void setModifyIndex(final int modifyIndex) { + this.modifyIndex = modifyIndex; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final LeafCertificate that = (LeafCertificate) o; + return createIndex == that.createIndex + && modifyIndex == that.modifyIndex + && Objects.equals(serialNumber, that.serialNumber) + && Objects.equals(certPEM, that.certPEM) + && Objects.equals(privateKeyPEM, that.privateKeyPEM) + && Objects.equals(service, that.service) + && Objects.equals(serviceURI, that.serviceURI) + && Objects.equals(validAfter, that.validAfter) + && Objects.equals(validBefore, that.validBefore) + && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(serialNumber, certPEM, privateKeyPEM, service, serviceURI, validAfter, validBefore, namespace, createIndex, modifyIndex); + } + + @Override + public String toString() { + return new StringJoiner(", ", LeafCertificate.class.getSimpleName() + "[", "]") + .add("serialNumber='" + serialNumber + "'") + .add("certPEM='" + certPEM + "'") + .add("privateKeyPEM='" + privateKeyPEM + "'") + .add("service='" + service + "'") + .add("serviceURI='" + serviceURI + "'") + .add("validAfter='" + validAfter + "'") + .add("validBefore='" + validBefore + "'") + .add("namespace='" + namespace + "'") + .add("createIndex=" + createIndex) + .add("modifyIndex=" + modifyIndex) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/ConnectClient.java b/src/main/java/com/ecwid/consul/v1/connect/ConnectClient.java new file mode 100644 index 0000000..54d105b --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/ConnectClient.java @@ -0,0 +1,16 @@ +package com.ecwid.consul.v1.connect; + +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.connect.model.CaConfigurationRequest; +import com.ecwid.consul.v1.connect.model.CaConfigurationResponse; +import com.ecwid.consul.v1.connect.model.CaRoots; + +public interface ConnectClient { + + Response connectListCaRoots(); + + Response connectGetCaConfiguration(); + + Response connectUpdateCaConfiguration(CaConfigurationRequest request); + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/ConnectConsulClient.java b/src/main/java/com/ecwid/consul/v1/connect/ConnectConsulClient.java new file mode 100644 index 0000000..5a75e12 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/ConnectConsulClient.java @@ -0,0 +1,80 @@ +package com.ecwid.consul.v1.connect; + +import com.ecwid.consul.json.GsonFactory; +import com.ecwid.consul.transport.HttpResponse; +import com.ecwid.consul.transport.TLSConfig; +import com.ecwid.consul.v1.ConsulRawClient; +import com.ecwid.consul.v1.OperationException; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.connect.model.CaConfigurationRequest; +import com.ecwid.consul.v1.connect.model.CaConfigurationResponse; +import com.ecwid.consul.v1.connect.model.CaRoots; + +public class ConnectConsulClient implements ConnectClient { + + private final ConsulRawClient rawClient; + + public ConnectConsulClient(ConsulRawClient rawClient) { + this.rawClient = rawClient; + } + + public ConnectConsulClient() { + this(new ConsulRawClient()); + } + + public ConnectConsulClient(TLSConfig tlsConfig) { + this(new ConsulRawClient(tlsConfig)); + } + + public ConnectConsulClient(String agentHost) { + this(new ConsulRawClient(agentHost)); + } + + public ConnectConsulClient(String agentHost, TLSConfig tlsConfig) { + this(new ConsulRawClient(agentHost, tlsConfig)); + } + + public ConnectConsulClient(String agentHost, int agentPort) { + this(new ConsulRawClient(agentHost, agentPort)); + } + + public ConnectConsulClient(String agentHost, int agentPort, TLSConfig tlsConfig) { + this(new ConsulRawClient(agentHost, agentPort, tlsConfig)); + } + + @Override + public Response connectListCaRoots() { + HttpResponse httpResponse = rawClient.makeGetRequest("/v1/connect/ca/roots"); + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson().fromJson(httpResponse.getContent(), CaRoots.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response connectGetCaConfiguration() { + HttpResponse httpResponse = rawClient.makeGetRequest("/v1/connect/ca/configuration"); + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson() + .fromJson(httpResponse.getContent(), CaConfigurationResponse.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response connectUpdateCaConfiguration(CaConfigurationRequest request) { + HttpResponse httpResponse = rawClient.makePutRequest( + "/v1/connect/ca/configuration", + GsonFactory.getGson().toJson(request)); + + if (httpResponse.getStatusCode() == 200) { + return new Response<>(GsonFactory.getGson() + .fromJson(httpResponse.getContent(), CaConfigurationResponse.class), httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionDeleteRequest.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionDeleteRequest.java new file mode 100644 index 0000000..8ea281f --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionDeleteRequest.java @@ -0,0 +1,54 @@ +package com.ecwid.consul.v1.connect.intentions; + +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionDeleteRequest { + + private String source; + + private String destination; + + public String getSource() { + return source; + } + + public void setSource(final String source) { + this.source = source; + } + + public String getDestination() { + return destination; + } + + public void setDestination(final String destination) { + this.destination = destination; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionDeleteRequest that = (IntentionDeleteRequest) o; + return Objects.equals(source, that.source) + && Objects.equals(destination, that.destination); + } + + @Override + public int hashCode() { + return Objects.hash(source, destination); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionDeleteRequest.class.getSimpleName() + "[", "]") + .add("source='" + source + "'") + .add("destination='" + destination + "'") + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionListRequest.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionListRequest.java new file mode 100644 index 0000000..7ee6571 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionListRequest.java @@ -0,0 +1,66 @@ +package com.ecwid.consul.v1.connect.intentions; + +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionListRequest { + + private String filter = ""; + + private String ns = ""; + + public IntentionListRequest() { + } + + public IntentionListRequest(final String filter) { + this.filter = filter; + } + + public IntentionListRequest(final String filter, final String ns) { + this.filter = filter; + this.ns = ns; + } + + public String getFilter() { + return filter; + } + + public void setFilter(final String filter) { + this.filter = filter; + } + + public String getNs() { + return ns; + } + + public void setNs(final String ns) { + this.ns = ns; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionListRequest that = (IntentionListRequest) o; + return Objects.equals(filter, that.filter) + && Objects.equals(ns, that.ns); + } + + @Override + public int hashCode() { + return Objects.hash(filter, ns); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionListRequest.class.getSimpleName() + "[", "]") + .add("filter='" + filter + "'") + .add("ns='" + ns + "'") + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionUpsertRequest.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionUpsertRequest.java new file mode 100644 index 0000000..369075a --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionUpsertRequest.java @@ -0,0 +1,79 @@ +package com.ecwid.consul.v1.connect.intentions; + +import com.ecwid.consul.v1.connect.intentions.model.Intention; +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionUpsertRequest { + + private String source; + + private String destination; + + private String ns = ""; + + private Intention intention; + + public String getSource() { + return source; + } + + public void setSource(final String source) { + this.source = source; + } + + public String getDestination() { + return destination; + } + + public void setDestination(final String destination) { + this.destination = destination; + } + + public String getNs() { + return ns; + } + + public void setNs(final String ns) { + this.ns = ns; + } + + public Intention getIntention() { + return intention; + } + + public void setIntention(final Intention intention) { + this.intention = intention; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionUpsertRequest that = (IntentionUpsertRequest) o; + return Objects.equals(source, that.source) + && Objects.equals(destination, that.destination) + && Objects.equals(ns, that.ns) + && Objects.equals(intention, that.intention); + } + + @Override + public int hashCode() { + return Objects.hash(source, destination, ns, intention); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionUpsertRequest.class.getSimpleName() + "[", "]") + .add("source='" + source + "'") + .add("destination='" + destination + "'") + .add("ns='" + ns + "'") + .add("intention=" + intention) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsClient.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsClient.java new file mode 100644 index 0000000..f1f58bb --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsClient.java @@ -0,0 +1,17 @@ +package com.ecwid.consul.v1.connect.intentions; + +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.connect.intentions.model.IntentionResponse; +import java.util.List; + +public interface IntentionsClient { + + Response createIntention(IntentionUpsertRequest request, String token); + + Response updateIntention(IntentionUpsertRequest request, String token); + + Response> listIntentions(IntentionListRequest request, String token); + + Response deleteIntention(IntentionDeleteRequest request, String token); + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsConsulClient.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsConsulClient.java new file mode 100644 index 0000000..0bb5524 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/IntentionsConsulClient.java @@ -0,0 +1,122 @@ +package com.ecwid.consul.v1.connect.intentions; + +import com.ecwid.consul.SingleUrlParameters; +import com.ecwid.consul.UrlParameters; +import com.ecwid.consul.json.GsonFactory; +import com.ecwid.consul.transport.HttpResponse; +import com.ecwid.consul.transport.TLSConfig; +import com.ecwid.consul.v1.ConsulRawClient; +import com.ecwid.consul.v1.OperationException; +import com.ecwid.consul.v1.Request; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.connect.intentions.model.IntentionResponse; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.util.List; + +public class IntentionsConsulClient implements IntentionsClient { + + private final ConsulRawClient rawClient; + + public IntentionsConsulClient(final ConsulRawClient rawClient) { + this.rawClient = rawClient; + } + + public IntentionsConsulClient() { + this(new ConsulRawClient()); + } + + public IntentionsConsulClient(TLSConfig tlsConfig) { + this(new ConsulRawClient(tlsConfig)); + } + + public IntentionsConsulClient(String agentHost) { + this(new ConsulRawClient(agentHost)); + } + + public IntentionsConsulClient(String agentHost, TLSConfig tlsConfig) { + this(new ConsulRawClient(agentHost, tlsConfig)); + } + + public IntentionsConsulClient(String agentHost, int agentPort) { + this(new ConsulRawClient(agentHost, agentPort)); + } + + public IntentionsConsulClient(String agentHost, int agentPort, TLSConfig tlsConfig) { + this(new ConsulRawClient(agentHost, agentPort, tlsConfig)); + } + + @Override + public Response createIntention(IntentionUpsertRequest request, String token) { + return updateIntention(request, token); + } + + @Override + public Response updateIntention(IntentionUpsertRequest request, String token) { + String json = GsonFactory.getGson().toJson(request.getIntention()); + UrlParameters tokenParam = token != null ? new SingleUrlParameters("token", token) : null; + UrlParameters sourceParam = new SingleUrlParameters("source", request.getSource()); + UrlParameters destinationParam = new SingleUrlParameters("destination", request.getDestination()); + UrlParameters nsParam = new SingleUrlParameters("ns", request.getNs()); + + HttpResponse httpResponse = rawClient.makePutRequest("/v1/connect/intentions/exact", json, + sourceParam, destinationParam, nsParam, tokenParam); + return processBooleanResponse(httpResponse); + } + + @Override + public Response> listIntentions(IntentionListRequest request, String token) { + UrlParameters tokenParam = token != null ? new SingleUrlParameters("token", token) : null; + UrlParameters filterParam = new SingleUrlParameters("filter", request.getFilter()); + UrlParameters nsParam = new SingleUrlParameters("ns", request.getNs()); + + HttpResponse httpResponse = rawClient.makeGetRequest("/v1/connect/intentions", + filterParam, nsParam, tokenParam); + if (httpResponse.getStatusCode() == 200) { + List value = GsonFactory.getGson().fromJson(httpResponse.getContent(), INTENTION_RESPONSE_TYPE); + return new Response<>(value, httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + @Override + public Response deleteIntention(IntentionDeleteRequest request, String token) { + UrlParameters sourceParam = new SingleUrlParameters("source", request.getSource()); + UrlParameters destinationParam = new SingleUrlParameters("destination", request.getDestination()); + + Request r = new Request.Builder() + .setEndpoint("/v1/connect/intentions/exact") + .addUrlParameter(sourceParam) + .addUrlParameter(destinationParam) + .setToken(token) + .build(); + HttpResponse httpResponse = rawClient.makeDeleteRequest(r); + return processBooleanResponse(httpResponse); + } + + private Response processBooleanResponse(HttpResponse httpResponse) { + if (httpResponse.getStatusCode() == 200) { + Boolean value = GsonFactory.getGson().fromJson(httpResponse.getContent(), BOOLEAN_TYPE); + if (!value) { + throw new OperationException(httpResponse); + } + return new Response<>(value, httpResponse); + } else { + throw new OperationException(httpResponse); + } + } + + private static final Type BOOLEAN_TYPE = new BooleanTypeToken().getType(); + + private static final Type INTENTION_RESPONSE_TYPE = new IntentionResponseType().getType(); + + private static class BooleanTypeToken extends TypeToken { + + } + + private static class IntentionResponseType extends TypeToken> { + + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/model/Intention.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/Intention.java new file mode 100644 index 0000000..dc72418 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/Intention.java @@ -0,0 +1,85 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; + +public class Intention { + + @SerializedName("SourceType") + private String sourceType = "consul"; + + @SerializedName("Action") + private String action; + + @SerializedName("Permissions") + private List permissions = new ArrayList<>(); + + @SerializedName("Description") + private String description; + + public String getSourceType() { + return sourceType; + } + + public void setSourceType(final String sourceType) { + this.sourceType = sourceType; + } + + public String getAction() { + return action; + } + + public void setAction(final String action) { + this.action = action; + } + + public List getPermissions() { + return permissions; + } + + public void setPermissions(final List permissions) { + this.permissions = permissions; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Intention intention = (Intention) o; + return Objects.equals(sourceType, intention.sourceType) + && Objects.equals(action, intention.action) + && Objects.equals(permissions, intention.permissions) + && Objects.equals(description, intention.description); + } + + @Override + public int hashCode() { + return Objects.hash(sourceType, action, permissions, description); + } + + @Override + public String toString() { + return new StringJoiner(", ", Intention.class.getSimpleName() + "[", "]") + .add("sourceType='" + sourceType + "'") + .add("action='" + action + "'") + .add("permissions=" + permissions) + .add("description='" + description + "'") + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpHeaderPermission.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpHeaderPermission.java new file mode 100644 index 0000000..10fc8bd --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpHeaderPermission.java @@ -0,0 +1,122 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionHttpHeaderPermission { + + @SerializedName("Name") + private String name; + + @SerializedName("Present") + private boolean present; + + @SerializedName("Exact") + private String exact; + + @SerializedName("Prefix") + private String prefix; + + @SerializedName("Suffix") + private String suffix; + + @SerializedName("Regex") + private String regex; + + @SerializedName("Invert") + private boolean invert; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public boolean isPresent() { + return present; + } + + public void setPresent(final boolean present) { + this.present = present; + } + + public String getExact() { + return exact; + } + + public void setExact(final String exact) { + this.exact = exact; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(final String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(final String suffix) { + this.suffix = suffix; + } + + public String getRegex() { + return regex; + } + + public void setRegex(final String regex) { + this.regex = regex; + } + + public boolean isInvert() { + return invert; + } + + public void setInvert(final boolean invert) { + this.invert = invert; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionHttpHeaderPermission that = (IntentionHttpHeaderPermission) o; + return present == that.present + && invert == that.invert + && Objects.equals(name, that.name) + && Objects.equals(exact, that.exact) + && Objects.equals(prefix, that.prefix) + && Objects.equals(suffix, that.suffix) + && Objects.equals(regex, that.regex); + } + + @Override + public int hashCode() { + return Objects.hash(name, present, exact, prefix, suffix, regex, invert); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionHttpHeaderPermission.class.getSimpleName() + "[", "]") + .add("name='" + name + "'") + .add("present=" + present) + .add("exact='" + exact + "'") + .add("prefix='" + prefix + "'") + .add("suffix='" + suffix + "'") + .add("regex='" + regex + "'") + .add("invert=" + invert) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpPermission.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpPermission.java new file mode 100644 index 0000000..f3c9121 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionHttpPermission.java @@ -0,0 +1,98 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionHttpPermission { + + @SerializedName("PathExact") + private String pathExact; + + @SerializedName("PathPrefix") + private String pathPrefix; + + @SerializedName("PathRegex") + private String pathRegex; + + @SerializedName("Methods") + private List methods = new ArrayList<>(); + + @SerializedName("Header") + private List header = new ArrayList<>(); + + public String getPathExact() { + return pathExact; + } + + public void setPathExact(final String pathExact) { + this.pathExact = pathExact; + } + + public String getPathPrefix() { + return pathPrefix; + } + + public void setPathPrefix(final String pathPrefix) { + this.pathPrefix = pathPrefix; + } + + public String getPathRegex() { + return pathRegex; + } + + public void setPathRegex(final String pathRegex) { + this.pathRegex = pathRegex; + } + + public List getMethods() { + return methods; + } + + public void setMethods(final List methods) { + this.methods = methods; + } + + public List getHeader() { + return header; + } + + public void setHeader(final List header) { + this.header = header; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionHttpPermission that = (IntentionHttpPermission) o; + return Objects.equals(pathExact, that.pathExact) + && Objects.equals(pathPrefix, that.pathPrefix) + && Objects.equals(pathRegex, that.pathRegex) + && Objects.equals(methods, that.methods) + && Objects.equals(header, that.header); + } + + @Override + public int hashCode() { + return Objects.hash(pathExact, pathPrefix, pathRegex, methods, header); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionHttpPermission.class.getSimpleName() + "[", "]") + .add("pathExact='" + pathExact + "'") + .add("pathPrefix='" + pathPrefix + "'") + .add("pathRegex='" + pathRegex + "'") + .add("methods=" + methods) + .add("header=" + header) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionPermission.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionPermission.java new file mode 100644 index 0000000..7f2b50b --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionPermission.java @@ -0,0 +1,57 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; +import java.util.StringJoiner; + +public class IntentionPermission { + + @SerializedName("Action") + private String action; + + @SerializedName("HTTP") + private IntentionHttpPermission http; + + public String getAction() { + return action; + } + + public void setAction(final String action) { + this.action = action; + } + + public IntentionHttpPermission getHttp() { + return http; + } + + public void setHttp(final IntentionHttpPermission http) { + this.http = http; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IntentionPermission that = (IntentionPermission) o; + return Objects.equals(action, that.action) + && Objects.equals(http, that.http); + } + + @Override + public int hashCode() { + return Objects.hash(action, http); + } + + @Override + public String toString() { + return new StringJoiner(", ", IntentionPermission.class.getSimpleName() + "[", "]") + .add("action='" + action + "'") + .add("http=" + http) + .toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionResponse.java b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionResponse.java new file mode 100644 index 0000000..5d4f18f --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/intentions/model/IntentionResponse.java @@ -0,0 +1,140 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Map; +import java.util.Objects; + +public class IntentionResponse extends Intention { + + @SerializedName("SourceNS") + private String sourceNs; + + @SerializedName("SourceName") + private String sourceName; + + @SerializedName("DestinationNS") + private String destinationNs; + + @SerializedName("DestinationName") + private String destinationName; + + @SerializedName("Meta") + private Map meta; + + @SerializedName("Precedence") + private int precedence; + + @SerializedName("CreateIndex") + private int createIndex; + + @SerializedName("ModifyIndex") + private int modifyIndex; + + public String getSourceNs() { + return sourceNs; + } + + public void setSourceNs(final String sourceNs) { + this.sourceNs = sourceNs; + } + + public String getSourceName() { + return sourceName; + } + + public void setSourceName(final String sourceName) { + this.sourceName = sourceName; + } + + public String getDestinationNs() { + return destinationNs; + } + + public void setDestinationNs(final String destinationNs) { + this.destinationNs = destinationNs; + } + + public String getDestinationName() { + return destinationName; + } + + public void setDestinationName(final String destinationName) { + this.destinationName = destinationName; + } + + public Map getMeta() { + return meta; + } + + public void setMeta(final Map meta) { + this.meta = meta; + } + + public int getPrecedence() { + return precedence; + } + + public void setPrecedence(final int precedence) { + this.precedence = precedence; + } + + public int getCreateIndex() { + return createIndex; + } + + public void setCreateIndex(final int createIndex) { + this.createIndex = createIndex; + } + + public int getModifyIndex() { + return modifyIndex; + } + + public void setModifyIndex(final int modifyIndex) { + this.modifyIndex = modifyIndex; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + final IntentionResponse that = (IntentionResponse) o; + return precedence == that.precedence + && createIndex == that.createIndex + && modifyIndex == that.modifyIndex + && Objects.equals(sourceNs, that.sourceNs) + && Objects.equals(sourceName, that.sourceName) + && Objects.equals(destinationNs, that.destinationNs) + && Objects.equals(destinationName, that.destinationName) + && Objects.equals(meta, that.meta); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), + sourceNs, sourceName, destinationNs, destinationName, + meta, precedence, createIndex, modifyIndex); + } + + @Override + public String toString() { + return "IntentionResponse{" + + "sourceNs='" + sourceNs + '\'' + + ", sourceName='" + sourceName + '\'' + + ", destinationNs='" + destinationNs + '\'' + + ", destinationName='" + destinationName + '\'' + + ", meta=" + meta + + ", precedence=" + precedence + + ", createIndex=" + createIndex + + ", modifyIndex=" + modifyIndex + + "} " + super.toString(); + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationRequest.java b/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationRequest.java new file mode 100644 index 0000000..65dad8c --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationRequest.java @@ -0,0 +1,70 @@ +package com.ecwid.consul.v1.connect.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Map; +import java.util.Objects; + +public class CaConfigurationRequest { + + @SerializedName("Provider") + private String provider; + + @SerializedName("Config") + private Map config; + + @SerializedName("ForceWithoutCrossSigning") + private boolean forceWithoutCrossSigning; + + public String getProvider() { + return provider; + } + + public void setProvider(final String provider) { + this.provider = provider; + } + + public Map getConfig() { + return config; + } + + public void setConfig(final Map config) { + this.config = config; + } + + public boolean isForceWithoutCrossSigning() { + return forceWithoutCrossSigning; + } + + public void setForceWithoutCrossSigning(final boolean forceWithoutCrossSigning) { + this.forceWithoutCrossSigning = forceWithoutCrossSigning; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CaConfigurationRequest that = (CaConfigurationRequest) o; + return forceWithoutCrossSigning == that.forceWithoutCrossSigning + && Objects.equals(provider, that.provider) + && Objects.equals(config, that.config); + } + + @Override + public int hashCode() { + return Objects.hash(provider, config, forceWithoutCrossSigning); + } + + @Override + public String toString() { + return "CaConfigurationRequest{" + + "provider='" + provider + '\'' + + ", config=" + config + + ", forceWithoutCrossSigning=" + forceWithoutCrossSigning + + '}'; + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationResponse.java b/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationResponse.java new file mode 100644 index 0000000..130381e --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/model/CaConfigurationResponse.java @@ -0,0 +1,83 @@ +package com.ecwid.consul.v1.connect.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Map; +import java.util.Objects; + +public class CaConfigurationResponse { + + @SerializedName("Provider") + private String provider; + + @SerializedName("Config") + private Map config; + + @SerializedName("CreateIndex") + private int createIndex; + + @SerializedName("ModifyIndex") + private int modifyIndex; + + public String getProvider() { + return provider; + } + + public void setProvider(final String provider) { + this.provider = provider; + } + + public Map getConfig() { + return config; + } + + public void setConfig(final Map config) { + this.config = config; + } + + public int getCreateIndex() { + return createIndex; + } + + public void setCreateIndex(final int createIndex) { + this.createIndex = createIndex; + } + + public int getModifyIndex() { + return modifyIndex; + } + + public void setModifyIndex(final int modifyIndex) { + this.modifyIndex = modifyIndex; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CaConfigurationResponse that = (CaConfigurationResponse) o; + return createIndex == that.createIndex + && modifyIndex == that.modifyIndex + && Objects.equals(provider, that.provider) + && Objects.equals(config, that.config); + } + + @Override + public int hashCode() { + return Objects.hash(provider, config, createIndex, modifyIndex); + } + + @Override + public String toString() { + return "CaConfigurationResponse{" + + "provider='" + provider + '\'' + + ", config=" + config + + ", createIndex=" + createIndex + + ", modifyIndex=" + modifyIndex + + '}'; + } + +} diff --git a/src/main/java/com/ecwid/consul/v1/connect/model/CaRoots.java b/src/main/java/com/ecwid/consul/v1/connect/model/CaRoots.java new file mode 100644 index 0000000..06bf3b6 --- /dev/null +++ b/src/main/java/com/ecwid/consul/v1/connect/model/CaRoots.java @@ -0,0 +1,46 @@ +package com.ecwid.consul.v1.connect.model; + +import com.google.gson.annotations.SerializedName; +import java.util.Objects; + +public class CaRoots extends com.ecwid.consul.v1.agent.model.CaRoots { + + @SerializedName("TrustDomain") + private String trustDomain; + + public String getTrustDomain() { + return trustDomain; + } + + public void setTrustDomain(final String trustDomain) { + this.trustDomain = trustDomain; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + final CaRoots caRoots = (CaRoots) o; + return Objects.equals(trustDomain, caRoots.trustDomain); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), trustDomain); + } + + @Override + public String toString() { + return "CaRoots{" + + "trustDomain='" + trustDomain + '\'' + + "} " + super.toString(); + } + +} diff --git a/src/test/java/com/ecwid/consul/TestUtils.java b/src/test/java/com/ecwid/consul/TestUtils.java new file mode 100644 index 0000000..5464e8a --- /dev/null +++ b/src/test/java/com/ecwid/consul/TestUtils.java @@ -0,0 +1,58 @@ +package com.ecwid.consul; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; + +public class TestUtils { + + public static final String LETS_ENCRYPT_X1 = "isrg-root-x1.pem"; + + public static final String LETS_ENCRYPT_X2 = "isrg-root-x2.pem"; + + public static Certificate getCertificate(String resourceName) throws IOException { + String certificateAsString = getCertificateAsString(resourceName); + try { + return CertificateFactory.getInstance("X509") + .generateCertificate( + new ByteArrayInputStream( + certificateAsString.getBytes(StandardCharsets.UTF_8))); + } catch (CertificateException ex) { + throw new IOException(ex); + } + } + + public static String getCertificateAsString(String resourceName) throws IOException { + return getResourceAsString(resourceName, TestUtils.class); + } + + public static String getResourceAsString(String resourceName, Class resourceClass) throws IOException { + try (InputStream is = resourceClass.getResourceAsStream(resourceName)) { + if (is == null) { + throw new IllegalArgumentException(String.format( + "Could not find resource [%s] for class [%s].", + resourceName, resourceClass)); + } + + return streamToString(is); + } + } + + public static String streamToString(InputStream stream) throws IOException { + StringBuilder builder = new StringBuilder(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + } + } + +} diff --git a/src/test/java/com/ecwid/consul/json/IntermediatesTest.java b/src/test/java/com/ecwid/consul/json/IntermediatesTest.java new file mode 100644 index 0000000..6926289 --- /dev/null +++ b/src/test/java/com/ecwid/consul/json/IntermediatesTest.java @@ -0,0 +1,45 @@ +package com.ecwid.consul.json; + +import com.ecwid.consul.TestUtils; +import com.ecwid.consul.v1.agent.model.CaRoots; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IntermediatesTest { + + @Test + public void testTwoIntermediatesDeserialization() throws IOException { + String intermediatesJson = resourceAsString("two-intermediates.json"); + CaRoots caRoot = GsonFactory.getGson().fromJson(intermediatesJson, CaRoots.class); + Assertions.assertEquals(2, caRoot.getRoots().get(0).getIntermediateCerts().size()); + } + + @Test + public void testEmptyIntermediatesDeserialization() throws IOException { + String intermediatesJson = resourceAsString("empty-intermediates.json"); + CaRoots caRoot = GsonFactory.getGson().fromJson(intermediatesJson, CaRoots.class); + Assertions.assertEquals(0, caRoot.getRoots().get(0).getIntermediateCerts().size()); + } + + @Test + public void testNullIntermediatesDeserialization() throws IOException { + String intermediatesJson = resourceAsString("null-intermediates.json"); + CaRoots caRoot = GsonFactory.getGson().fromJson(intermediatesJson, CaRoots.class); + Assertions.assertNull(caRoot.getRoots().get(0).getIntermediateCerts()); + } + + private String resourceAsString(String resource) throws IOException { + try (InputStream stream = getClass().getResourceAsStream(resource)) { + if (stream == null) { + throw new IllegalStateException(String.format("Missing %s resource.", resource)); + } + return TestUtils.streamToString(stream); + } + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/AuthorizeConsulClientTest.java b/src/test/java/com/ecwid/consul/v1/agent/AuthorizeConsulClientTest.java new file mode 100644 index 0000000..58ac9d8 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/AuthorizeConsulClientTest.java @@ -0,0 +1,62 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.ConsulTestConstants; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.agent.model.AuthorizeRequest; +import com.ecwid.consul.v1.agent.model.AuthorizeResponse; +import com.pszymczyk.consul.ConsulProcess; +import com.pszymczyk.consul.ConsulStarterBuilder; +import com.pszymczyk.consul.infrastructure.Ports; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AuthorizeConsulClientTest { + + private static final String ACL_MASTER_TOKEN = "mastertoken"; + + private ConsulProcess consul; + + private final int port = Ports.nextAvailable(); + + private final AgentConsulClient consulClient = new AgentConsulClient("localhost", port); + + @BeforeEach + public void setUp() { + String customConfiguration = + "{ \"acl_master_token\": \"" + ACL_MASTER_TOKEN + "\"" + + ", \"acl_datacenter\": \"dc-test\"" + + ", \"datacenter\": \"dc-test\" }"; + + consul = ConsulStarterBuilder.consulStarter() + .withConsulVersion(ConsulTestConstants.CONSUL_VERSION) + .withHttpPort(port) + .withCustomConfig(customConfiguration) + .build() + .start(); + } + + @AfterEach + public void tearDown() { + consul.close(); + } + + @Test + public void testAuthorize() { + AuthorizeRequest request = new AuthorizeRequest(); + request.setTarget("db"); + request.setClientCertUri("spiffe://dc1-7e567ac2-551d-463f-8497-f78972856fc1.consul/ns/default/dc/dc1/svc/web"); + request.setClientCertSerial("04:00:00:00:00:01:15:4b:5a:c3:94"); + + Response response = consulClient.agentAuthorize(request); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getValue()); + + AuthorizeResponse responseValue = response.getValue(); + + Assertions.assertTrue(responseValue.isAuthorized()); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/AuthorizeDtoTest.java b/src/test/java/com/ecwid/consul/v1/agent/AuthorizeDtoTest.java new file mode 100644 index 0000000..05cbd59 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/AuthorizeDtoTest.java @@ -0,0 +1,26 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.v1.agent.model.AuthorizeRequest; +import com.ecwid.consul.v1.agent.model.AuthorizeResponse; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class AuthorizeDtoTest { + + @Nested + class EqualsHashCode { + + @Test + void authorizeRequest() { + EqualsVerifier.simple().forClass(AuthorizeRequest.class).verify(); + } + + @Test + void authorizeResponse() { + EqualsVerifier.simple().forClass(AuthorizeResponse.class).verify(); + } + + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/CaRootsConsulClientTest.java b/src/test/java/com/ecwid/consul/v1/agent/CaRootsConsulClientTest.java new file mode 100644 index 0000000..315b4d3 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/CaRootsConsulClientTest.java @@ -0,0 +1,57 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.ConsulTestConstants; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.agent.model.CaRoots; +import com.pszymczyk.consul.ConsulProcess; +import com.pszymczyk.consul.ConsulStarterBuilder; +import com.pszymczyk.consul.infrastructure.Ports; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CaRootsConsulClientTest { + + private static final String ACL_MASTER_TOKEN = "mastertoken"; + + private ConsulProcess consul; + + private final int port = Ports.nextAvailable(); + + private final AgentConsulClient consulClient = new AgentConsulClient("localhost", port); + + @BeforeEach + public void setUp() { + String customConfiguration = + "{ \"acl_master_token\": \"" + ACL_MASTER_TOKEN + "\"" + + ", \"acl_datacenter\": \"dc-test\"" + + ", \"datacenter\": \"dc-test\" }"; + + consul = ConsulStarterBuilder.consulStarter() + .withConsulVersion(ConsulTestConstants.CONSUL_VERSION) + .withHttpPort(port) + .withCustomConfig(customConfiguration) + .build() + .start(); + } + + @AfterEach + public void tearDown() { + consul.close(); + } + + @Test + public void testCaRoots() { + Response response = consulClient.agentCaRoots(); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getValue()); + + CaRoots responseValue = response.getValue(); + + Assertions.assertEquals(1, responseValue.getRoots().size()); + Assertions.assertEquals(responseValue.getActiveRootId(), responseValue.getRoots().get(0).getId()); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/CaRootsDtoTest.java b/src/test/java/com/ecwid/consul/v1/agent/CaRootsDtoTest.java new file mode 100644 index 0000000..e9d1fa2 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/CaRootsDtoTest.java @@ -0,0 +1,39 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.TestUtils; +import com.ecwid.consul.v1.agent.model.CaRoot; +import com.ecwid.consul.v1.agent.model.CaRoots; +import java.io.IOException; +import java.security.cert.Certificate; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class CaRootsDtoTest { + + @Nested + class EqualsHashCode { + + @Test + void caRoots() throws IOException { + EqualsVerifier.simple() + .forClass(CaRoots.class) + .withPrefabValues(Certificate.class, + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X1), + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X2)) + .verify(); + } + + @Test + void caRoot() throws IOException { + EqualsVerifier.simple() + .forClass(CaRoot.class) + .withPrefabValues(Certificate.class, + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X1), + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X2)) + .verify(); + } + + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateConsulClientTest.java b/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateConsulClientTest.java new file mode 100644 index 0000000..96ba03c --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateConsulClientTest.java @@ -0,0 +1,59 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.ConsulTestConstants; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.agent.model.AuthorizeRequest; +import com.ecwid.consul.v1.agent.model.AuthorizeResponse; +import com.ecwid.consul.v1.agent.model.LeafCertificate; +import com.pszymczyk.consul.ConsulProcess; +import com.pszymczyk.consul.ConsulStarterBuilder; +import com.pszymczyk.consul.infrastructure.Ports; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LeafCertificateConsulClientTest { + + private static final String ACL_MASTER_TOKEN = "mastertoken"; + + private ConsulProcess consul; + + private final int port = Ports.nextAvailable(); + + private final AgentConsulClient consulClient = new AgentConsulClient("localhost", port); + + @BeforeEach + public void setUp() { + String customConfiguration = + "{ \"acl_master_token\": \"" + ACL_MASTER_TOKEN + "\"" + + ", \"acl_datacenter\": \"dc-test\"" + + ", \"datacenter\": \"dc-test\" }"; + + consul = ConsulStarterBuilder.consulStarter() + .withConsulVersion(ConsulTestConstants.CONSUL_VERSION) + .withHttpPort(port) + .withCustomConfig(customConfiguration) + .build() + .start(); + } + + @AfterEach + public void tearDown() { + consul.close(); + } + + @Test + public void testLeafCertificate() { + Response response = consulClient.agentLeafCertificate("db"); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getValue()); + + LeafCertificate responseValue = response.getValue(); + + Assertions.assertNotNull(responseValue.getCertPEM()); + Assertions.assertNotNull(responseValue.getPrivateKeyPEM()); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateDtoTest.java b/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateDtoTest.java new file mode 100644 index 0000000..2f61bb7 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/agent/LeafCertificateDtoTest.java @@ -0,0 +1,20 @@ +package com.ecwid.consul.v1.agent; + +import com.ecwid.consul.v1.agent.model.LeafCertificate; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class LeafCertificateDtoTest { + + @Nested + class EqualsHashCode { + + @Test + void leafCertificate() { + EqualsVerifier.simple().forClass(LeafCertificate.class).verify(); + } + + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/connect/ConnectConsulClientTest.java b/src/test/java/com/ecwid/consul/v1/connect/ConnectConsulClientTest.java new file mode 100644 index 0000000..f483f65 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/connect/ConnectConsulClientTest.java @@ -0,0 +1,77 @@ +package com.ecwid.consul.v1.connect; + +import com.ecwid.consul.ConsulTestConstants; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.connect.model.CaConfigurationRequest; +import com.ecwid.consul.v1.connect.model.CaConfigurationResponse; +import com.ecwid.consul.v1.connect.model.CaRoots; +import com.pszymczyk.consul.ConsulProcess; +import com.pszymczyk.consul.ConsulStarterBuilder; +import com.pszymczyk.consul.infrastructure.Ports; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ConnectConsulClientTest { + + private static final String ACL_MASTER_TOKEN = "mastertoken"; + + private ConsulProcess consul; + + private final int port = Ports.nextAvailable(); + + private final ConnectConsulClient consulClient = new ConnectConsulClient("localhost", port); + + @BeforeEach + public void setUp() { + String customConfiguration = + "{ \"acl_master_token\": \"" + ACL_MASTER_TOKEN + "\"" + + ", \"acl_datacenter\": \"dc-test\"" + + ", \"datacenter\": \"dc-test\" }"; + + consul = ConsulStarterBuilder.consulStarter() + .withConsulVersion(ConsulTestConstants.CONSUL_VERSION) + .withHttpPort(port) + .withCustomConfig(customConfiguration) + .build() + .start(); + } + + @AfterEach + public void tearDown() { + consul.close(); + } + + @Test + public void testListCaRoots() { + Response response = consulClient.connectListCaRoots(); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getValue()); + + CaRoots responseValue = response.getValue(); + + Assertions.assertNotNull(responseValue.getTrustDomain()); + Assertions.assertFalse(responseValue.getRoots().isEmpty()); + } + + @Test + public void testGetCaConfiguration() { + Response response = consulClient.connectGetCaConfiguration(); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getValue()); + } + + @Test + public void testUpdateCaConfiguration() { + Response caConfiguration = consulClient.connectGetCaConfiguration(); + CaConfigurationRequest request = new CaConfigurationRequest(); + request.setProvider(caConfiguration.getValue().getProvider()); + request.setConfig(caConfiguration.getValue().getConfig()); + Response response = consulClient.connectUpdateCaConfiguration(request); + Assertions.assertNotNull(response); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/connect/intentions/EqualsVerifierTest.java b/src/test/java/com/ecwid/consul/v1/connect/intentions/EqualsVerifierTest.java new file mode 100644 index 0000000..9aa33f2 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/connect/intentions/EqualsVerifierTest.java @@ -0,0 +1,23 @@ +package com.ecwid.consul.v1.connect.intentions; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +public class EqualsVerifierTest { + + @Test + public void verifyIntentionDeleteRequest() { + EqualsVerifier.simple().forClass(IntentionDeleteRequest.class).verify(); + } + + @Test + public void verifyIntentionListRequest() { + EqualsVerifier.simple().forClass(IntentionListRequest.class).verify(); + } + + @Test + public void verifyIntentionUpsertRequest() { + EqualsVerifier.simple().forClass(IntentionUpsertRequest.class).verify(); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/connect/intentions/model/EqualsVerifierTest.java b/src/test/java/com/ecwid/consul/v1/connect/intentions/model/EqualsVerifierTest.java new file mode 100644 index 0000000..d628f53 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/connect/intentions/model/EqualsVerifierTest.java @@ -0,0 +1,33 @@ +package com.ecwid.consul.v1.connect.intentions.model; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; + +public class EqualsVerifierTest { + + @Test + public void verifyIntention() { + EqualsVerifier.simple().forClass(Intention.class).verify(); + } + + @Test + public void verifyIntentionHttpHeaderPermission() { + EqualsVerifier.simple().forClass(IntentionHttpHeaderPermission.class).verify(); + } + + @Test + public void verifyIntentionHttpPermission() { + EqualsVerifier.simple().forClass(IntentionHttpPermission.class).verify(); + } + + @Test + public void verifyIntentionPermission() { + EqualsVerifier.simple().forClass(IntentionPermission.class).verify(); + } + + @Test + public void verifyIntentionResponse() { + EqualsVerifier.simple().forClass(IntentionResponse.class).verify(); + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/connect/model/CaConfigurationDtoTest.java b/src/test/java/com/ecwid/consul/v1/connect/model/CaConfigurationDtoTest.java new file mode 100644 index 0000000..abc5001 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/connect/model/CaConfigurationDtoTest.java @@ -0,0 +1,24 @@ +package com.ecwid.consul.v1.connect.model; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class CaConfigurationDtoTest { + + @Nested + class EqualsHashCode { + + @Test + void caConfigurationResponse() { + EqualsVerifier.simple().forClass(CaConfigurationResponse.class).verify(); + } + + @Test + void caConfigurationRequest() { + EqualsVerifier.simple().forClass(CaConfigurationRequest.class).verify(); + } + + } + +} diff --git a/src/test/java/com/ecwid/consul/v1/connect/model/CaRootsDtoTest.java b/src/test/java/com/ecwid/consul/v1/connect/model/CaRootsDtoTest.java new file mode 100644 index 0000000..6843410 --- /dev/null +++ b/src/test/java/com/ecwid/consul/v1/connect/model/CaRootsDtoTest.java @@ -0,0 +1,27 @@ +package com.ecwid.consul.v1.connect.model; + +import com.ecwid.consul.TestUtils; +import java.io.IOException; +import java.security.cert.Certificate; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class CaRootsDtoTest { + + @Nested + class EqualsHashCode { + + @Test + void caRoots() throws IOException { + EqualsVerifier.simple() + .forClass(CaRoots.class) + .withPrefabValues(Certificate.class, + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X1), + TestUtils.getCertificate(TestUtils.LETS_ENCRYPT_X2)) + .verify(); + } + + } + +} diff --git a/src/test/resources/com/ecwid/consul/isrg-root-x1.pem b/src/test/resources/com/ecwid/consul/isrg-root-x1.pem new file mode 100644 index 0000000..b85c803 --- /dev/null +++ b/src/test/resources/com/ecwid/consul/isrg-root-x1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/src/test/resources/com/ecwid/consul/isrg-root-x2.pem b/src/test/resources/com/ecwid/consul/isrg-root-x2.pem new file mode 100644 index 0000000..7d903ed --- /dev/null +++ b/src/test/resources/com/ecwid/consul/isrg-root-x2.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- diff --git a/src/test/resources/com/ecwid/consul/json/empty-intermediates.json b/src/test/resources/com/ecwid/consul/json/empty-intermediates.json new file mode 100644 index 0000000..8cc489a --- /dev/null +++ b/src/test/resources/com/ecwid/consul/json/empty-intermediates.json @@ -0,0 +1,22 @@ +{ + "ActiveRootID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "TrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d.consul", + "Roots": [ + { + "ID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "Name": "Consul CA Root Cert", + "SerialNumber": 10, + "SigningKeyID": "63:63:3a:61:32:3a:37:64:3a:66:38:3a:63:30:3a:33:61:3a:30:36:3a:36:35:3a:66:32:3a:32:38:3a:35:39:3a:64:38:3a:35:38:3a:36:38:3a:39:62:3a:65:64:3a:35:62:3a:36:65:3a:36:30:3a:32:31:3a:33:35:3a:34:37:3a:65:64:3a:36:33:3a:38:33:3a:61:31:3a:38:38:3a:64:37:3a:65:65:3a:61:30:3a:35:62:3a:39:31", + "ExternalTrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d", + "NotBefore": "2021-09-24T07:45:47Z", + "NotAfter": "2031-09-24T07:45:47Z", + "RootCert": "-----BEGIN CERTIFICATE-----\nMIICWzCCAgGgAwIBAgIBCjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxDb25zdWwg\nQ0EgMTAwHhcNMjEwOTI0MDc0NTQ3WhcNMzEwOTI0MDc0NTQ3WjAXMRUwEwYDVQQD\nEwxDb25zdWwgQ0EgMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARAXWkwLdS+\nJ/X5H0K/ER6MQSRGg7SIIxv5p4rZPbt46vEXzPVrYeinZcZeRelDWEUPFfpnZuDt\ni2WOUCfMrKoAo4IBPDCCATgwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8waAYDVR0OBGEEX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1OmYyOjI4OjU5OmQ4\nOjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYzOjgzOmExOjg4OmQ3\nOmVlOmEwOjViOjkxMGoGA1UdIwRjMGGAX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1\nOmYyOjI4OjU5OmQ4OjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYz\nOjgzOmExOjg4OmQ3OmVlOmEwOjViOjkxMD8GA1UdEQQ4MDaGNHNwaWZmZTovLzgw\nMjc4YjllLWYzZWMtZjRkOS0yNzdiLTIzMWU1OWEwNDY2ZC5jb25zdWwwCgYIKoZI\nzj0EAwIDSAAwRQIgQEOEUicNVKPYXw790b1vDTEXUp5li3nQ8ZEv4N9jwUYCIQDR\nF0YYCABtY3ipgnAK5UGU15fpW8AHqpnRKfXUjtdxvg==\n-----END CERTIFICATE-----\n", + "IntermediateCerts": [], + "Active": true, + "PrivateKeyType": "", + "PrivateKeyBits": 0, + "CreateIndex": 11, + "ModifyIndex": 11 + } + ] +} diff --git a/src/test/resources/com/ecwid/consul/json/null-intermediates.json b/src/test/resources/com/ecwid/consul/json/null-intermediates.json new file mode 100644 index 0000000..0ec0dc7 --- /dev/null +++ b/src/test/resources/com/ecwid/consul/json/null-intermediates.json @@ -0,0 +1,22 @@ +{ + "ActiveRootID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "TrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d.consul", + "Roots": [ + { + "ID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "Name": "Consul CA Root Cert", + "SerialNumber": 10, + "SigningKeyID": "63:63:3a:61:32:3a:37:64:3a:66:38:3a:63:30:3a:33:61:3a:30:36:3a:36:35:3a:66:32:3a:32:38:3a:35:39:3a:64:38:3a:35:38:3a:36:38:3a:39:62:3a:65:64:3a:35:62:3a:36:65:3a:36:30:3a:32:31:3a:33:35:3a:34:37:3a:65:64:3a:36:33:3a:38:33:3a:61:31:3a:38:38:3a:64:37:3a:65:65:3a:61:30:3a:35:62:3a:39:31", + "ExternalTrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d", + "NotBefore": "2021-09-24T07:45:47Z", + "NotAfter": "2031-09-24T07:45:47Z", + "RootCert": "-----BEGIN CERTIFICATE-----\nMIICWzCCAgGgAwIBAgIBCjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxDb25zdWwg\nQ0EgMTAwHhcNMjEwOTI0MDc0NTQ3WhcNMzEwOTI0MDc0NTQ3WjAXMRUwEwYDVQQD\nEwxDb25zdWwgQ0EgMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARAXWkwLdS+\nJ/X5H0K/ER6MQSRGg7SIIxv5p4rZPbt46vEXzPVrYeinZcZeRelDWEUPFfpnZuDt\ni2WOUCfMrKoAo4IBPDCCATgwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8waAYDVR0OBGEEX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1OmYyOjI4OjU5OmQ4\nOjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYzOjgzOmExOjg4OmQ3\nOmVlOmEwOjViOjkxMGoGA1UdIwRjMGGAX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1\nOmYyOjI4OjU5OmQ4OjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYz\nOjgzOmExOjg4OmQ3OmVlOmEwOjViOjkxMD8GA1UdEQQ4MDaGNHNwaWZmZTovLzgw\nMjc4YjllLWYzZWMtZjRkOS0yNzdiLTIzMWU1OWEwNDY2ZC5jb25zdWwwCgYIKoZI\nzj0EAwIDSAAwRQIgQEOEUicNVKPYXw790b1vDTEXUp5li3nQ8ZEv4N9jwUYCIQDR\nF0YYCABtY3ipgnAK5UGU15fpW8AHqpnRKfXUjtdxvg==\n-----END CERTIFICATE-----\n", + "IntermediateCerts": null, + "Active": true, + "PrivateKeyType": "", + "PrivateKeyBits": 0, + "CreateIndex": 11, + "ModifyIndex": 11 + } + ] +} diff --git a/src/test/resources/com/ecwid/consul/json/two-intermediates.json b/src/test/resources/com/ecwid/consul/json/two-intermediates.json new file mode 100644 index 0000000..e861654 --- /dev/null +++ b/src/test/resources/com/ecwid/consul/json/two-intermediates.json @@ -0,0 +1,25 @@ +{ + "ActiveRootID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "TrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d.consul", + "Roots": [ + { + "ID": "4e:ea:da:22:73:7a:f9:90:0c:bd:c8:a0:75:4c:3d:40:3c:82:71:59", + "Name": "Consul CA Root Cert", + "SerialNumber": 10, + "SigningKeyID": "63:63:3a:61:32:3a:37:64:3a:66:38:3a:63:30:3a:33:61:3a:30:36:3a:36:35:3a:66:32:3a:32:38:3a:35:39:3a:64:38:3a:35:38:3a:36:38:3a:39:62:3a:65:64:3a:35:62:3a:36:65:3a:36:30:3a:32:31:3a:33:35:3a:34:37:3a:65:64:3a:36:33:3a:38:33:3a:61:31:3a:38:38:3a:64:37:3a:65:65:3a:61:30:3a:35:62:3a:39:31", + "ExternalTrustDomain": "80278b9e-f3ec-f4d9-277b-231e59a0466d", + "NotBefore": "2021-09-24T07:45:47Z", + "NotAfter": "2031-09-24T07:45:47Z", + "RootCert": "-----BEGIN CERTIFICATE-----\nMIICWzCCAgGgAwIBAgIBCjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxDb25zdWwg\nQ0EgMTAwHhcNMjEwOTI0MDc0NTQ3WhcNMzEwOTI0MDc0NTQ3WjAXMRUwEwYDVQQD\nEwxDb25zdWwgQ0EgMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARAXWkwLdS+\nJ/X5H0K/ER6MQSRGg7SIIxv5p4rZPbt46vEXzPVrYeinZcZeRelDWEUPFfpnZuDt\ni2WOUCfMrKoAo4IBPDCCATgwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8waAYDVR0OBGEEX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1OmYyOjI4OjU5OmQ4\nOjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYzOjgzOmExOjg4OmQ3\nOmVlOmEwOjViOjkxMGoGA1UdIwRjMGGAX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1\nOmYyOjI4OjU5OmQ4OjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYz\nOjgzOmExOjg4OmQ3OmVlOmEwOjViOjkxMD8GA1UdEQQ4MDaGNHNwaWZmZTovLzgw\nMjc4YjllLWYzZWMtZjRkOS0yNzdiLTIzMWU1OWEwNDY2ZC5jb25zdWwwCgYIKoZI\nzj0EAwIDSAAwRQIgQEOEUicNVKPYXw790b1vDTEXUp5li3nQ8ZEv4N9jwUYCIQDR\nF0YYCABtY3ipgnAK5UGU15fpW8AHqpnRKfXUjtdxvg==\n-----END CERTIFICATE-----\n", + "IntermediateCerts": [ + "-----BEGIN CERTIFICATE-----\nMIICWzCCAgGgAwIBAgIBCjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxDb25zdWwg\nQ0EgMTAwHhcNMjEwOTI0MDc0NTQ3WhcNMzEwOTI0MDc0NTQ3WjAXMRUwEwYDVQQD\nEwxDb25zdWwgQ0EgMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARAXWkwLdS+\nJ/X5H0K/ER6MQSRGg7SIIxv5p4rZPbt46vEXzPVrYeinZcZeRelDWEUPFfpnZuDt\ni2WOUCfMrKoAo4IBPDCCATgwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8waAYDVR0OBGEEX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1OmYyOjI4OjU5OmQ4\nOjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYzOjgzOmExOjg4OmQ3\nOmVlOmEwOjViOjkxMGoGA1UdIwRjMGGAX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1\nOmYyOjI4OjU5OmQ4OjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYz\nOjgzOmExOjg4OmQ3OmVlOmEwOjViOjkxMD8GA1UdEQQ4MDaGNHNwaWZmZTovLzgw\nMjc4YjllLWYzZWMtZjRkOS0yNzdiLTIzMWU1OWEwNDY2ZC5jb25zdWwwCgYIKoZI\nzj0EAwIDSAAwRQIgQEOEUicNVKPYXw790b1vDTEXUp5li3nQ8ZEv4N9jwUYCIQDR\nF0YYCABtY3ipgnAK5UGU15fpW8AHqpnRKfXUjtdxvg==\n-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\nMIICWzCCAgGgAwIBAgIBCjAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxDb25zdWwg\nQ0EgMTAwHhcNMjEwOTI0MDc0NTQ3WhcNMzEwOTI0MDc0NTQ3WjAXMRUwEwYDVQQD\nEwxDb25zdWwgQ0EgMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARAXWkwLdS+\nJ/X5H0K/ER6MQSRGg7SIIxv5p4rZPbt46vEXzPVrYeinZcZeRelDWEUPFfpnZuDt\ni2WOUCfMrKoAo4IBPDCCATgwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB\nAf8waAYDVR0OBGEEX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1OmYyOjI4OjU5OmQ4\nOjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYzOjgzOmExOjg4OmQ3\nOmVlOmEwOjViOjkxMGoGA1UdIwRjMGGAX2NjOmEyOjdkOmY4OmMwOjNhOjA2OjY1\nOmYyOjI4OjU5OmQ4OjU4OjY4OjliOmVkOjViOjZlOjYwOjIxOjM1OjQ3OmVkOjYz\nOjgzOmExOjg4OmQ3OmVlOmEwOjViOjkxMD8GA1UdEQQ4MDaGNHNwaWZmZTovLzgw\nMjc4YjllLWYzZWMtZjRkOS0yNzdiLTIzMWU1OWEwNDY2ZC5jb25zdWwwCgYIKoZI\nzj0EAwIDSAAwRQIgQEOEUicNVKPYXw790b1vDTEXUp5li3nQ8ZEv4N9jwUYCIQDR\nF0YYCABtY3ipgnAK5UGU15fpW8AHqpnRKfXUjtdxvg==\n-----END CERTIFICATE-----\n" + ], + "Active": true, + "PrivateKeyType": "", + "PrivateKeyBits": 0, + "CreateIndex": 11, + "ModifyIndex": 11 + } + ] +}