aboutsummaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-05-21 09:59:15 +0200
committerMorten Tokle <mortent@verizonmedia.com>2021-05-25 08:01:51 +0200
commit05c8138b4936a3144e8b95348ebc525148ba709f (patch)
tree4927e74bc5159c5ee401eb08388c1776b24907bb /configserver
parent7986918b9c04158010fbc0b7c68e76d2c3a53ed3 (diff)
Include operator certificates in application trust store
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java11
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java33
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java11
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java1
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java33
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java3
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java31
10 files changed, 151 insertions, 8 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index d5b0c6fce4a..e48848417fc 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -37,6 +37,7 @@ import com.yahoo.vespa.flags.UnboundFlag;
import java.io.File;
import java.net.URI;
+import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -281,6 +282,7 @@ public class ModelContextImpl implements ModelContext {
private final SecretStore secretStore;
private final StringFlag jvmGCOptionsFlag;
private final boolean allowDisableMtls;
+ private final List<X509Certificate> operatorCertificates;
public Properties(ApplicationId applicationId,
ConfigserverConfig configserverConfig,
@@ -294,7 +296,8 @@ public class ModelContextImpl implements ModelContext {
Optional<ApplicationRoles> applicationRoles,
Optional<Quota> maybeQuota,
List<TenantSecretStore> tenantSecretStores,
- SecretStore secretStore) {
+ SecretStore secretStore,
+ List<X509Certificate> operatorCertificates) {
this.featureFlags = new FeatureFlags(flagSource, applicationId);
this.applicationId = applicationId;
this.multitenant = configserverConfig.multitenant() || configserverConfig.hostedVespa() || Boolean.getBoolean("multitenant");
@@ -317,6 +320,7 @@ public class ModelContextImpl implements ModelContext {
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm());
this.allowDisableMtls = PermanentFlags.ALLOW_DISABLE_MTLS.bindTo(flagSource)
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
+ this.operatorCertificates = operatorCertificates;
}
@Override public ModelContext.FeatureFlags featureFlags() { return featureFlags; }
@@ -385,6 +389,11 @@ public class ModelContextImpl implements ModelContext {
return allowDisableMtls;
}
+ @Override
+ public List<X509Certificate> operatorCertificates() {
+ return operatorCertificates;
+ }
+
public String flagValueForClusterType(StringFlag flag, Optional<ClusterSpec.Type> clusterType) {
return clusterType.map(type -> flag.with(CLUSTER_TYPE, type.name()))
.orElse(flag)
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
index 5c0207878f1..ada278709fd 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
@@ -37,7 +37,9 @@ import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
+import java.security.cert.X509Certificate;
import java.util.Comparator;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
@@ -163,7 +165,8 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
.readApplicationRoles(applicationId),
zkClient.readQuota(),
zkClient.readTenantSecretStores(),
- secretStore);
+ secretStore,
+ zkClient.readOperatorCertificates());
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
index ea2a525b440..1b43e57c01a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.config.server.session;
+import com.google.common.collect.ImmutableList;
import com.yahoo.component.Version;
import com.yahoo.config.model.api.ApplicationRoles;
import com.yahoo.config.model.api.ContainerEndpoint;
@@ -11,6 +12,8 @@ import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.slime.ArrayTraverser;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
@@ -20,14 +23,18 @@ import com.yahoo.vespa.config.server.http.SessionHandler;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointSerializer;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataSerializer;
import com.yahoo.vespa.config.server.tenant.TenantSecretStoreSerializer;
+import org.eclipse.jetty.util.ssl.X509;
+import java.security.cert.X509Certificate;
import java.time.Clock;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Parameters for preparing an application. Immutable.
@@ -52,6 +59,7 @@ public final class PrepareParams {
static final String TENANT_SECRET_STORES_PARAM_NAME = "tenantSecretStores";
static final String FORCE_PARAM_NAME = "force";
static final String WAIT_FOR_RESOURCES_IN_PREPARE = "waitForResourcesInPrepare";
+ static final String OPERATOR_CERTIFICATES = "operatorCertificates";
private final ApplicationId applicationId;
private final TimeoutBudget timeoutBudget;
@@ -69,6 +77,7 @@ public final class PrepareParams {
private final Optional<ApplicationRoles> applicationRoles;
private final Optional<Quota> quota;
private final List<TenantSecretStore> tenantSecretStores;
+ private final List<X509Certificate> operatorCertificates;
private PrepareParams(ApplicationId applicationId, TimeoutBudget timeoutBudget, boolean ignoreValidationErrors,
boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion,
@@ -76,7 +85,7 @@ public final class PrepareParams {
Optional<EndpointCertificateMetadata> endpointCertificateMetadata,
Optional<DockerImage> dockerImageRepository, Optional<AthenzDomain> athenzDomain,
Optional<ApplicationRoles> applicationRoles, Optional<Quota> quota, List<TenantSecretStore> tenantSecretStores,
- boolean force, boolean waitForResourcesInPrepare) {
+ boolean force, boolean waitForResourcesInPrepare, List<X509Certificate> operatorCertificates) {
this.timeoutBudget = timeoutBudget;
this.applicationId = Objects.requireNonNull(applicationId);
this.ignoreValidationErrors = ignoreValidationErrors;
@@ -93,6 +102,7 @@ public final class PrepareParams {
this.tenantSecretStores = tenantSecretStores;
this.force = force;
this.waitForResourcesInPrepare = waitForResourcesInPrepare;
+ this.operatorCertificates = operatorCertificates;
}
public static class Builder {
@@ -113,6 +123,7 @@ public final class PrepareParams {
private Optional<ApplicationRoles> applicationRoles = Optional.empty();
private Optional<Quota> quota = Optional.empty();
private List<TenantSecretStore> tenantSecretStores = List.of();
+ private List<X509Certificate> operatorCertificates = List.of();
public Builder() { }
@@ -245,11 +256,17 @@ public final class PrepareParams {
return this;
}
+ public Builder withOperatorCertificates(List<X509Certificate> operatorCertificates) {
+ this.operatorCertificates = List.copyOf(operatorCertificates);
+ return this;
+ }
+
public PrepareParams build() {
return new PrepareParams(applicationId, timeoutBudget, ignoreValidationErrors, dryRun,
verbose, isBootstrap, vespaVersion, containerEndpoints,
endpointCertificateMetadata, dockerImageRepository, athenzDomain,
- applicationRoles, quota, tenantSecretStores, force, waitForResourcesInPrepare);
+ applicationRoles, quota, tenantSecretStores, force, waitForResourcesInPrepare,
+ operatorCertificates);
}
}
@@ -292,6 +309,7 @@ public final class PrepareParams {
.tenantSecretStores(SlimeUtils.optionalString(params.field(TENANT_SECRET_STORES_PARAM_NAME)).orElse(null))
.force(booleanValue(params, FORCE_PARAM_NAME))
.waitForResourcesInPrepare(booleanValue(params, WAIT_FOR_RESOURCES_IN_PREPARE))
+ .withOperatorCertificates(deserialize(params.field(OPERATOR_CERTIFICATES), PrepareParams::readOperatorCertificates, Collections.emptyList()))
.build();
}
@@ -343,6 +361,13 @@ public final class PrepareParams {
return Optional.ofNullable(request.getProperty(propertyName));
}
+ private static List<X509Certificate> readOperatorCertificates(Inspector array) {
+ return SlimeUtils.entriesStream(array)
+ .map(Inspector::asString)
+ .map(X509CertificateUtils::fromPem)
+ .collect(Collectors.toList());
+ }
+
public String getApplicationName() {
return applicationId.application().value();
}
@@ -400,4 +425,8 @@ public final class PrepareParams {
public List<TenantSecretStore> tenantSecretStores() {
return tenantSecretStores;
}
+
+ public List<X509Certificate> operatorCertificates() {
+ return operatorCertificates;
+ }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
index f1044b28049..542b54d877e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java
@@ -17,6 +17,7 @@ import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@@ -137,6 +138,10 @@ public abstract class Session implements Comparable<Session> {
sessionZooKeeperClient.writeTenantSecretStores(tenantSecretStores);
}
+ public void setOperatorCertificates(List<X509Certificate> operatorCertificates) {
+ sessionZooKeeperClient.writeOperatorCertificates(operatorCertificates);
+ }
+
/** Returns application id read from ZooKeeper. Will throw RuntimeException if not found */
public ApplicationId getApplicationId() {
return sessionZooKeeperClient.readApplicationId()
@@ -172,6 +177,10 @@ public abstract class Session implements Comparable<Session> {
return sessionZooKeeperClient.readTenantSecretStores();
}
+ public List<X509Certificate> getOperatorCertificates() {
+ return sessionZooKeeperClient.readOperatorCertificates();
+ }
+
private Transaction createSetStatusTransaction(Status status) {
return sessionZooKeeperClient.createWriteStatusTransaction(status);
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index 30cdc0f6e8a..9360a2b1a2a 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -51,6 +51,7 @@ import com.yahoo.vespa.flags.FlagSource;
import java.io.File;
import java.io.IOException;
+import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
@@ -204,7 +205,8 @@ public class SessionPreparer {
applicationRoles,
params.quota(),
params.tenantSecretStores(),
- secretStore);
+ secretStore,
+ params.operatorCertificates());
this.fileDistributionProvider = fileDistributionFactory.createProvider(serverDbSessionDir);
this.preparedModelsBuilder = new PreparedModelsBuilder(modelFactoryRegistry,
permanentApplicationPackage,
@@ -277,7 +279,8 @@ public class SessionPreparer {
prepareResult.allocatedHosts(),
athenzDomain,
params.quota(),
- params.tenantSecretStores());
+ params.tenantSecretStores(),
+ params.operatorCertificates());
checkTimeout("write state to zookeeper");
}
@@ -327,7 +330,8 @@ public class SessionPreparer {
AllocatedHosts allocatedHosts,
Optional<AthenzDomain> athenzDomain,
Optional<Quota> quota,
- List<TenantSecretStore> tenantSecretStores) {
+ List<TenantSecretStore> tenantSecretStores,
+ List<X509Certificate> operatorCertificates) {
ZooKeeperDeployer zkDeployer = zooKeeperClient.createDeployer(deployLogger);
try {
zkDeployer.deploy(applicationPackage, fileRegistryMap, allocatedHosts);
@@ -339,6 +343,7 @@ public class SessionPreparer {
zooKeeperClient.writeAthenzDomain(athenzDomain);
zooKeeperClient.writeQuota(quota);
zooKeeperClient.writeTenantSecretStores(tenantSecretStores);
+ zooKeeperClient.writeOperatorCertificates(operatorCertificates);
} catch (RuntimeException | IOException e) {
zkDeployer.cleanup();
throw new RuntimeException("Error preparing session", e);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
index cb46d65c4c5..0386b1ca4ef 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java
@@ -259,6 +259,7 @@ public class SessionRepository {
session.setDockerImageRepository(existingSession.getDockerImageRepository());
session.setAthenzDomain(existingSession.getAthenzDomain());
session.setTenantSecretStores(existingSession.getTenantSecretStores());
+ session.setOperatorCertificates(existingSession.getOperatorCertificates());
return session;
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
index c7c4f1926d7..c3d6bba0ac2 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionZooKeeperClient.java
@@ -21,6 +21,7 @@ import com.yahoo.transaction.Transaction;
import com.yahoo.vespa.config.server.UserConfigDefinitionRepo;
import com.yahoo.vespa.config.server.deploy.ZooKeeperClient;
import com.yahoo.vespa.config.server.deploy.ZooKeeperDeployer;
+import com.yahoo.vespa.config.server.tenant.OperatorCertificateSerializer;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
import com.yahoo.vespa.config.server.tenant.TenantSecretStoreSerializer;
import com.yahoo.vespa.config.server.zookeeper.ConfigCurator;
@@ -29,6 +30,7 @@ import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.curator.transaction.CuratorOperations;
import com.yahoo.vespa.curator.transaction.CuratorTransaction;
+import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@@ -57,6 +59,7 @@ public class SessionZooKeeperClient {
private static final String ATHENZ_DOMAIN = "athenzDomain";
private static final String QUOTA_PATH = "quota";
private static final String TENANT_SECRET_STORES_PATH = "tenantSecretStores";
+ private static final String OPERATOR_CERTIFICATES_PATH = "operatorCertificates";
private final Curator curator;
private final ConfigCurator configCurator;
@@ -191,6 +194,10 @@ public class SessionZooKeeperClient {
return sessionPath.append(TENANT_SECRET_STORES_PATH).getAbsolute();
}
+ private String operatorCertificatesPath() {
+ return sessionPath.append(OPERATOR_CERTIFICATES_PATH).getAbsolute();
+ }
+
public void writeVespaVersion(Version version) {
configCurator.putData(versionPath(), version.toString());
}
@@ -282,6 +289,21 @@ public class SessionZooKeeperClient {
.orElse(List.of());
}
+ public void writeOperatorCertificates(List<X509Certificate> certificates) {
+ if( ! certificates.isEmpty()) {
+ var bytes = uncheck(() -> SlimeUtils.toJsonBytes(OperatorCertificateSerializer.toSlime(certificates)));
+ configCurator.putData(operatorCertificatesPath(), bytes);
+ }
+ }
+
+ public List<X509Certificate> readOperatorCertificates() {
+ if ( ! configCurator.exists(operatorCertificatesPath())) return List.of();
+ return Optional.ofNullable(configCurator.getData(operatorCertificatesPath()))
+ .map(SlimeUtils::jsonToSlime)
+ .map(slime -> OperatorCertificateSerializer.fromSlime(slime.get()))
+ .orElse(List.of());
+ }
+
/**
* Create necessary paths atomically for a new session.
*
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java
new file mode 100644
index 00000000000..7fe2ab5e12f
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/OperatorCertificateSerializer.java
@@ -0,0 +1,33 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.model.api.ApplicationRoles;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.Slime;
+import com.yahoo.slime.SlimeUtils;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class OperatorCertificateSerializer {
+
+ public static Slime toSlime(List<X509Certificate> certificateList) {
+ Slime slime = new Slime();
+ Cursor array = slime.setArray();
+ certificateList.stream()
+ .map(X509CertificateUtils::toPem)
+ .forEach(array::addString);
+ return slime;
+ }
+
+ public static List<X509Certificate> fromSlime(Inspector array) {
+ return SlimeUtils.entriesStream(array)
+ .map(Inspector::asString)
+ .map(X509CertificateUtils::fromPem)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
index 7b9420b6b9e..0acf4404326 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ModelContextImplTest.java
@@ -74,7 +74,8 @@ public class ModelContextImplTest {
Optional.empty(),
Optional.empty(),
List.of(),
- new SecretStoreProvider().get()),
+ new SecretStoreProvider().get(),
+ List.of()),
Optional.empty(),
Optional.empty(),
new Version(7),
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
index f50238f2b85..06ff9f4b3f6 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java
@@ -7,6 +7,11 @@ import com.yahoo.config.model.api.EndpointCertificateMetadata;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.TenantName;
import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.SignatureAlgorithm;
+import com.yahoo.security.X509CertificateBuilder;
+import com.yahoo.security.X509CertificateUtils;
import com.yahoo.slime.ArrayInserter;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Injector;
@@ -20,10 +25,16 @@ import com.yahoo.vespa.config.server.tenant.ContainerEndpointSerializer;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataSerializer;
import org.junit.Test;
+import javax.security.auth.x500.X500Principal;
import java.io.IOException;
+import java.math.BigInteger;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -179,6 +190,26 @@ public class PrepareParamsTest {
assertPrepareParamsEqual(urlPrepareParams, jsonPrepareParams);
}
+ @Test
+ public void testOperatorCertificates() throws IOException {
+ Slime slime = SlimeUtils.jsonToSlime(json);
+ Cursor cursor = slime.get();
+ Cursor array = cursor.setArray(PrepareParams.OPERATOR_CERTIFICATES);
+
+ KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ X500Principal subject = new X500Principal("CN=myservice");
+ X509Certificate cert =
+ X509CertificateBuilder.fromKeypair(keyPair, subject, Instant.now(),
+ Instant.now().plus(1, ChronoUnit.DAYS), SignatureAlgorithm.SHA256_WITH_ECDSA,
+ BigInteger.valueOf(1))
+ .setBasicConstraints(true, true)
+ .build();
+ array.addString(X509CertificateUtils.toPem(cert));
+ PrepareParams prepareParams = PrepareParams.fromJson(SlimeUtils.toJsonBytes(slime), TenantName.from("foo"), Duration.ofSeconds(60));
+ assertEquals(1, prepareParams.operatorCertificates().size());
+ assertEquals(cert, prepareParams.operatorCertificates().get(0));
+ }
+
private void assertPrepareParamsEqual(PrepareParams urlParams, PrepareParams jsonParams) {
assertEquals(urlParams.ignoreValidationErrors(), jsonParams.ignoreValidationErrors());
assertEquals(urlParams.isDryRun(), jsonParams.isDryRun());